code:
local function get_formspec(name, width, height)
	local sizewidth = 8
	local txpos = (sizewidth-width)/2
	if width >= 8 then
		sizewidth = width
		txpos = 0
	end
	local bag_formspec =
		"size[".. sizewidth ..",".. height+5 .."]" ..
		default.gui_bg ..
		default.gui_bg_img ..
		default.gui_slots ..
		"list[detached:"..name..";main;".. txpos ..",0;".. width ..",".. height ..";]"..
		"list[current_player;main;".. (sizewidth-8)/2 ..",".. height+0.85 ..";8,1;]" ..
		"list[current_player;main;".. (sizewidth-8)/2 ..",".. height+2.08 ..";8,3;8]" ..
		"listring[detached:"..name..";main]"..
		"listring[current_player;main]" ..
		default.get_hotbar_bg((sizewidth-8)/2,height+0.85)
	return bag_formspec
end

local function inv_to_table(inv)
	local t = {}
	for listname, list in pairs(inv:get_lists()) do
		local size = inv:get_size(listname)
		if size then
			t[listname] = {}
			for i = 1, size, 1 do
				t[listname][i] = inv:get_stack(listname, i):to_table()
			end
		end
	end
	return t
end

local function table_to_inv(inv, t)
	for listname, list in pairs(t) do
		for i, stack in pairs(list) do
			inv:set_stack(listname, i, stack)
		end
	end
end

-- functions to handle upgrades (can be overridden)

-- called on bag_inv_add_item, bag_inv_remove_item and save_bag_inv_itemstack (called on on_move, on_put and on_take)
function inventorybags.on_change_bag_inv(bagstack, baginv)
	return bagstack, baginv
end

-- called on open bag
function inventorybags.on_open_bag(bagstack, baginv, player)
	return bagstack, baginv, player
end

-- called on close bag
function inventorybags.on_close_bag(player, fields, name, formname, sound)
	return player, fields, name, formname, sound
end

-- called before open bag
function inventorybags.before_open_bag(itemstack, user, width, height, sound)
	return itemstack, user, width, height, sound
end

-- called on use bag
function inventorybags.on_use_bag(itemstack, user, pointed_thing)
	return itemstack, user, pointed_thing
end

-- called on drop bag
function inventorybags.on_drop_bag(itemstack, dropper, pos)
	minetest.item_drop(itemstack, dropper, pos)
	return itemstack, dropper, pos
end

local function save_bag_inv_itemstack(inv, stack)
	stack, inv = inventorybags.on_change_bag_inv(stack, inv)
	local meta = stack:get_meta()
	meta:set_string("inventorybags_inv_content", minetest.serialize(inv_to_table(inv)))
	return stack
end

local function save_bag_inv(inv, player)
	local playerinv = minetest.get_inventory{type="player", name=player:get_player_name()}
	local bag_id = inv:get_location().name
	local listname = "main"
	local size = playerinv:get_size(listname)
	for i = 1, size, 1 do
		local stack = playerinv:get_stack(listname, i)
		local meta = stack:get_meta()
		if meta:get_string("inventorybags_bag_identity") == bag_id then
			stack = save_bag_inv_itemstack(inv, stack)
			playerinv:set_stack(listname, i, stack)
		end
	end
end

local mod_storage = minetest.get_mod_storage()
local function create_invname(itemstack)
	local counter = mod_storage:get_int("counter", value) or 0
	counter = counter + 1
	mod_storage:set_int("counter", counter)
	return itemstack:get_name().."_C_"..counter
end

local function stack_to_player_inv(stack, player)
	local payerinv = player:get_inventory()
	if payerinv:room_for_item("main", stack) then
		payerinv:add_item("main", stack)
	else
		minetest.item_drop(stack, player, player:get_pos())
	end
end

local function open_bag(itemstack, user, width, height, sound)
	itemstack, user, width, height, sound = inventorybags.before_open_bag(itemstack, user, width, height, sound)
	local allow_bag_input = false
	if minetest.get_item_group(itemstack:get_name(), "bag_bag") > 0 then
		allow_bag_input = true
	end
	local meta = itemstack:get_meta()
	local playername = user:get_player_name()
	local invname = meta:get_string("inventorybags_bag_identity")

	-- bag identity
	if invname == "" then
		local item_count = itemstack:get_count()
		if item_count > 1 then
			local newitemstack = itemstack:take_item(item_count-1)
			minetest.after(0.01, stack_to_player_inv, newitemstack, user)
		end
		invname = create_invname(itemstack)
		meta:set_string("inventorybags_bag_identity", invname)
	end

	meta:set_int("inventorybags_width", width)
	meta:set_int("inventorybags_height", height)

	local inv = minetest.create_detached_inventory(invname, {
		allow_put = function(inv, listname, index, stack, player)
			if allow_bag_input then
				if minetest.get_item_group(stack:get_name(), "bag_bag") > 0 then
					return 0
				end
			else
				if minetest.get_item_group(stack:get_name(), "bag") > 0 then
					return 0
				end
			end
			return stack:get_count()
		end,
		on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
			save_bag_inv(inv, player)
		end,
		on_put = function(inv, listname, index, stack, player)
			save_bag_inv(inv, player)
		end,
		on_take = function(inv, listname, index, stack, player)
			-- fix swap bug
			local size = inv:get_size(listname)
			for i = 1, size, 1 do
				local stack = inv:get_stack(listname, i)
				local remove_stack = false
				if allow_bag_input then
					if minetest.get_item_group(stack:get_name(), "bag_bag") > 0 then
						remove_stack = true
					end
				else
					if minetest.get_item_group(stack:get_name(), "bag") > 0 then
						remove_stack = true
					end
				end
				if remove_stack == true then
					inv:set_stack(listname, i, "")
					local playerinv = player:get_inventory()
					if playerinv:room_for_item("main", stack) then
						playerinv:add_item("main", stack)
					else
						minetest.item_drop(save_bag_inv_itemstack(inv, stack), player, player:get_pos())
						minetest.close_formspec(player:get_player_name(), inv:get_location().name)
					end
				end
			end
			save_bag_inv(inv, player)
		end,
	}, playername)
	inv:set_size("main", width*height)
	local invmetastring = meta:get_string("inventorybags_inv_content")
	if invmetastring ~= "" then
		table_to_inv(inv, minetest.deserialize(invmetastring))

		itemstack, inv, user = inventorybags.on_open_bag(itemstack, inv, user)
		save_bag_inv_itemstack(inv, itemstack)
	end

	if sound then
		minetest.sound_play(sound, {gain = 0.8, object = user, max_hear_distance = 5})
	end
	minetest.show_formspec(playername, invname, get_formspec(invname, width, height))
	return itemstack
end

function inventorybags.bag_inv_add_item(bagstack, itemstack)
	local meta = bagstack:get_meta()
	local invname = meta:get_string("inventorybags_bag_identity")
	if not invname then
		return false
	end
	local inv = minetest.create_detached_inventory(invname, {})
	local width = meta:get_int("inventorybags_width")
	local height = meta:get_int("inventorybags_height")
	inv:set_size("main", width*height)
	local invmetastring = meta:get_string("inventorybags_inv_content")
	if invmetastring ~= "" then
		table_to_inv(inv, minetest.deserialize(invmetastring))

		bagstack, inv = inventorybags.on_change_bag_inv(bagstack, inv)
	end
	if inv:room_for_item("main", itemstack) then
		inv:add_item("main", itemstack)
		return save_bag_inv_itemstack(inv, bagstack)
	end
	return false
end

function inventorybags.bag_inv_remove_item(bagstack, itemstack)
	local meta = bagstack:get_meta()
	local invname = meta:get_string("inventorybags_bag_identity")
	if not invname then
		return false
	end
	local inv = minetest.create_detached_inventory(invname, {})
	local width = meta:get_int("inventorybags_width")
	local height = meta:get_int("inventorybags_height")
	inv:set_size("main", width*height)
	local invmetastring = meta:get_string("inventorybags_inv_content")
	if invmetastring ~= "" then
		table_to_inv(inv, minetest.deserialize(invmetastring))

		bagstack, inv = inventorybags.on_change_bag_inv(bagstack, inv)
	end
	if inv:contains_item("main", itemstack) then
		inv:remove_item("main", itemstack)
		return save_bag_inv_itemstack(inv, bagstack)
	end
	return false
end

function inventorybags.register_bag(name, bagtable)
	minetest.register_craftitem(name, {
		description = bagtable.description,
		inventory_image = bagtable.inventory_image,
		groups = {bag = 1},

		on_secondary_use = function(itemstack, user)
			return open_bag(itemstack, user, bagtable.width, bagtable.height, bagtable.sound_open)
		end,
		on_place = function(itemstack, placer, pointed_thing)
			return open_bag(itemstack, placer, bagtable.width, bagtable.height, bagtable.sound_open)
		end,
		on_use = function(itemstack, user, pointed_thing)
			return inventorybags.on_use_bag(itemstack, user, pointed_thing)
		end,
		on_drop = function(itemstack, dropper, pos)
			return inventorybags.on_drop_bag(itemstack, dropper, pos)
		end
	})

	minetest.register_on_player_receive_fields(function(player, formname, fields)
		local nisformn = string.find(formname, name.."_C_")
		if nisformn == 1 then
			if fields.quit then
				player, fields, name, formname, sound = inventorybags.on_close_bag(player, fields, name, formname, bagtable.sound_close)
				if bagtable.sound_close then
					minetest.sound_play(sound, {gain = 0.8, object = player, max_hear_distance = 5})
				end
			end
		end
		return
	end)
end

inventorybags.register_bag("inventorybags:tiny_pouch", {
	description = "Tiny Pouch",
	inventory_image = "inventorybags_tiny_pouch.png",
	width = 1,
	height = 1,
	sound_open = "inventorybags_open_bag",
	sound_close = "inventorybags_close_bag"
})

inventorybags.register_bag("inventorybags:small_pouch", {
	description = "Small Pouch",
	inventory_image = "inventorybags_small_pouch.png",
	width = 2,
	height = 2,
	sound_open = "inventorybags_open_bag",
	sound_close = "inventorybags_close_bag"
})

inventorybags.register_bag("inventorybags:medium_pouch", {
	description = "Medium Pouch",
	inventory_image = "inventorybags_medium_pouch.png",
	width = 3,
	height = 3,
	sound_open = "inventorybags_open_bag",
	sound_close = "inventorybags_close_bag"
})

inventorybags.register_bag("inventorybags:large_pouch", {
	description = "Large Pouch",
	inventory_image = "inventorybags_large_pouch.png",
	width = 4,
	height = 4,
	sound_open = "inventorybags_open_bag",
	sound_close = "inventorybags_close_bag"
})

inventorybags.register_bag("inventorybags:backpack", {
	description = "Backpack",
	inventory_image = "inventorybags_backpack.png",
	width = 8,
	height = 4,
	sound_open = "inventorybags_open_bag",
	sound_close = "inventorybags_close_bag"
})

inventorybags.register_bag("inventorybags:suitcase", {
	description = "Suitcase",
	inventory_image = "inventorybags_suitcase.png",
	width = 8,
	height = 4,
	sound_open = "inventorybags_open_suitcase",
	sound_close = "inventorybags_open_suitcase"
})

inventorybags.register_bag("inventorybags:bag_on_a_stick", {
	description = "Bag on a Stick",
	inventory_image = "inventorybags_bag_on_a_stick.png",
	width = 8,
	height = 4,
	sound_open = "inventorybags_open_bag",
	sound_close = "inventorybags_close_bag"
})

inventorybags.register_bag("inventorybags:belt_bag", {
	description = "Belt Bag",
	inventory_image = "inventorybags_belt_bag.png",
	width = 8,
	height = 4,
	sound_open = "inventorybags_open_zipper",
	sound_close = "inventorybags_close_zipper"
})

inventorybags.register_bag("inventorybags:chest_bag", {
	description = "Chest Bag",
	inventory_image = "inventorybags_chest_bag.png",
	width = 8,
	height = 4,
	sound_open = "default_chest_open",
	sound_close = "default_chest_close"
})

inventorybags.register_bag("inventorybags:hand_bag", {
	description = "Hand Bag",
	inventory_image = "inventorybags_hand_bag.png",
	width = 8,
	height = 4,
	sound_open = "inventorybags_open_bag",
	sound_close = "inventorybags_close_bag"
})

inventorybags.register_bag("inventorybags:satchel", {
	description = "Satchel",
	inventory_image = "inventorybags_satchel.png",
	width = 8,
	height = 4,
	sound_open = "inventorybags_open_bag",
	sound_close = "inventorybags_close_bag"
})

inventorybags.register_bag("inventorybags:paper_bag", {
	description = "Paper Bag",
	inventory_image = "inventorybags_paper_bag.png",
	width = 2,
	height = 3,
	sound_open = "inventorybags_open_plastic_bag",
	sound_close = "inventorybags_close_plastic_bag"
})

if minetest.registered_items["homedecor:plastic_sheeting"] then
	inventorybags.register_bag("inventorybags:plastic_bag", {
		description = "Plastic Bag",
		inventory_image = "inventorybags_plastic_bag.png",
		width = 2,
		height = 3,
		sound_open = "inventorybags_open_plastic_bag",
		sound_close = "inventorybags_close_plastic_bag"
	})
end

-- special bags

minetest.register_craftitem("inventorybags:bag_transporting_bag", {
	description = "Bag Transporting Bag",
	inventory_image = "inventorybags_bag_transporting_bag.png",
	groups = {bag = 1, bag_bag = 1},

	on_secondary_use = function(itemstack, user)
		return open_bag(itemstack, user, 2, 2, "inventorybags_open_bag")
	end,
	on_place = function(itemstack, placer, pointed_thing)
		return open_bag(itemstack, placer, 2, 2, "inventorybags_open_bag")
	end,
	on_use = function(itemstack, user, pointed_thing)
		return inventorybags.on_use_bag(itemstack, user, pointed_thing)
	end,
	on_drop = function(itemstack, dropper, pos)
		return inventorybags.on_drop_bag(itemstack, dropper, pos)
	end
})

minetest.register_on_player_receive_fields(function(player, formname, fields)
	local nisformn = string.find(formname, "inventorybags:bag_transporting_bag_C_")
	if nisformn == 1 then
		if fields.quit then
			player, fields, name, formname, sound = inventorybags.on_close_bag(player, fields, name, formname, "inventorybags_close_bag")
			minetest.sound_play(sound, {gain = 0.8, object = player, max_hear_distance = 5})
		end
	end
	return
end)

if minetest.setting_getbool("inventorybags_enable_item_teleportation_bag") then

	local function open_teleport_bag_inv(itemstack, placer, pointed_thing)
		local location = minetest.deserialize(itemstack:get_meta():get_string("inventorybags_formspec_location"))
		if location then
			minetest.sound_play("inventorybags_open_teleportation_bag", {gain = 0.8, object = placer, max_hear_distance = 5})
			local node = minetest.get_node(location)
			-- load the area
			local vmie, vmae = VoxelManip():read_from_map(location, location)
			if not node then
				minetest.chat_send_player(placer:get_player_name(), "Error: area not loaded")
				return itemstack
			end
			local rkfunction = minetest.registered_nodes[node.name].on_rightclick
			if rkfunction then
				rkfunction(location, node, placer, itemstack, pointed_thing)
			else
				local nodemeta = minetest.get_meta(location)
				local formspec = nodemeta:get_string("formspec")
				if formspec then
					-- set inv locations
					local textbnc = string.find(formspec, "context;")
					while textbnc and string.sub(formspec, textbnc-5, textbnc-1) == "list[" do
						formspec = string.sub(formspec, 1, textbnc-1).."nodemeta:"..
							location.x..","..location.y..","..location.z..string.sub(formspec, textbnc+7)
						textbnc = string.find(formspec, "context;")
					end
					-- deprecated but still used
					local textbnd = string.find(formspec, "current_name;")
					while textbnd and string.sub(formspec, textbnd-5, textbnd-1) == "list[" do
						formspec = string.sub(formspec, 1, textbnd-1).."nodemeta:"..
							location.x..","..location.y..","..location.z..string.sub(formspec, textbnd+12)
						textbnd = string.find(formspec, "current_name;")
					end
					minetest.show_formspec(placer:get_player_name(), "inventorybags:item_teleportation_bag", formspec)
				end
			end
		end
		return itemstack
	end

	minetest.register_craftitem("inventorybags:item_teleportation_bag", {
		description = "Item Teleportation Bag",
		inventory_image = "inventorybags_item_teleportation_bag.png",
		groups = {bag = 1},

		on_use = function(itemstack, user, pointed_thing)
			if user:get_player_control().sneak == true then
				if pointed_thing and pointed_thing.type == "node" then
					itemstack:get_meta():set_string("inventorybags_formspec_location", minetest.serialize(pointed_thing.under))
					minetest.chat_send_player(user:get_player_name(), "Item Teleportation Bag target set to: "..
						pointed_thing.under.x..", "..pointed_thing.under.y..", "..pointed_thing.under.z..
						" ("..minetest.get_node(pointed_thing.under).name..")"
					)
				end
			else
				minetest.chat_send_player(user:get_player_name(), "Hold sneak and left click to set the target of your Item Teleportation Bag")
			end
			return itemstack
		end,

		on_secondary_use = function(itemstack, user, pointed_thing)
			return open_teleport_bag_inv(itemstack, user, pointed_thing)
		end,

		on_place = function(itemstack, placer, pointed_thing)
			return open_teleport_bag_inv(itemstack, placer, pointed_thing)
		end,
	})

	minetest.register_on_player_receive_fields(function(player, formname, fields)
		local nisformn = string.find(formname, "inventorybags:item_teleportation_bag")
		if nisformn == 1 then
			if fields.quit then
				minetest.sound_play("inventorybags_close_teleportation_bag", {gain = 0.8, object = player, max_hear_distance = 5})
			end
		end
		return
	end)
end

if minetest.get_modpath("xdecor") then
	minetest.register_craftitem("inventorybags:ender_bag", {
		description = "Ender Bag",
		inventory_image = "inventorybags_ender_bag.png",
		groups = {bag = 1},

		on_secondary_use = function(itemstack, user)
			minetest.sound_play("inventorybags_open_teleportation_bag", {gain = 0.8, object = user, max_hear_distance = 5})
			minetest.show_formspec(user:get_player_name(), "inventorybags:ender_bag",
				"size[8,9]" ..
				default.gui_bg ..
				default.gui_bg_img ..
				default.gui_slots ..
				"list[current_player;enderchest;0,0.3;8,4;]" ..
				"list[current_player;main;0,4.85;8,1;]" ..
				"list[current_player;main;0,6.08;8,3;8]" ..
				"listring[current_player;enderchest]" ..
				"listring[current_player;main]" ..
				default.get_hotbar_bg(0,4.85)
			)
			return itemstack
		end,
		on_place = function(itemstack, placer, pointed_thing)
			minetest.sound_play("inventorybags_open_teleportation_bag", {gain = 0.8, object = placer, max_hear_distance = 5})
			minetest.show_formspec(placer:get_player_name(), "inventorybags:ender_bag",
				"size[8,9]" ..
				default.gui_bg ..
				default.gui_bg_img ..
				default.gui_slots ..
				"list[current_player;enderchest;0,0.3;8,4;]" ..
				"list[current_player;main;0,4.85;8,1;]" ..
				"list[current_player;main;0,6.08;8,3;8]" ..
				"listring[current_player;enderchest]" ..
				"listring[current_player;main]" ..
				default.get_hotbar_bg(0,4.85)
			)
			return itemstack
		end
	})

	minetest.register_on_player_receive_fields(function(player, formname, fields)
		local nisformn = string.find(formname, "inventorybags:ender_bag")
		if nisformn == 1 then
			if fields.quit then
				minetest.sound_play("inventorybags_close_teleportation_bag", {gain = 0.8, object = player, max_hear_distance = 5})
			end
		end
		return
	end)
end

if minetest.get_modpath("more_chests") then
	minetest.register_craftitem("inventorybags:wifi_bag", {
		description = "Wifi Bag",
		inventory_image = "inventorybags_wifi_bag.png",
		groups = {bag = 1},

		on_secondary_use = function(itemstack, user)
			minetest.sound_play("inventorybags_open_teleportation_bag", {gain = 0.8, object = user, max_hear_distance = 5})
			minetest.show_formspec(user:get_player_name(), "inventorybags:wifi_bag",
				"size[8,9]" ..
				default.gui_bg ..
				default.gui_bg_img ..
				default.gui_slots ..
				"list[current_player;more_chests:wifi;0,0.3;8,4;]" ..
				"list[current_player;main;0,4.85;8,1;]" ..
				"list[current_player;main;0,6.08;8,3;8]" ..
				"listring[current_player;more_chests:wifi]" ..
				"listring[current_player;main]" ..
				default.get_hotbar_bg(0,4.85)
			)
			return itemstack
		end,
		on_place = function(itemstack, placer, pointed_thing)
			minetest.sound_play("inventorybags_open_teleportation_bag", {gain = 0.8, object = placer, max_hear_distance = 5})
			minetest.show_formspec(placer:get_player_name(), "inventorybags:wifi_bag",
				"size[8,9]" ..
				default.gui_bg ..
				default.gui_bg_img ..
				default.gui_slots ..
				"list[current_player;more_chests:wifi;0,0.3;8,4;]" ..
				"list[current_player;main;0,4.85;8,1;]" ..
				"list[current_player;main;0,6.08;8,3;8]" ..
				"listring[current_player;more_chests:wifi]" ..
				"listring[current_player;main]" ..
				default.get_hotbar_bg(0,4.85)
			)
			return itemstack
		end
	})
	minetest.register_on_player_receive_fields(function(player, formname, fields)
		local nisformn = string.find(formname, "inventorybags:wifi_bag")
		if nisformn == 1 then
			if fields.quit then
				minetest.sound_play("inventorybags_close_teleportation_bag", {gain = 0.8, object = player, max_hear_distance = 5})
			end
		end
		return
	end)
end

if minetest.get_modpath("beds") then
	minetest.register_craftitem("inventorybags:sleeping_bag", {
		description = "Sleeping Bag",
		inventory_image = "inventorybags_sleeping_bag.png",
		groups = {bag = 1},

		on_secondary_use = function(itemstack, user)
			minetest.sound_play("inventorybags_sleeping", {gain = 1, object = user, max_hear_distance = 5})
			beds.on_rightclick(user:get_pos(), user)
		end,
		on_place = function(itemstack, placer, pointed_thing)
			minetest.sound_play("inventorybags_sleeping", {gain = 1, object = placer, max_hear_distance = 5})
			beds.on_rightclick(placer:get_pos(), placer)
		end
	})
end

if not minetest.setting_getbool("inventorybags_dialable_bag_of_winds") then

	local gravity_change = -1.5

	local function set_physics_normal(player)
		local physics = player:get_physics_override()
		physics.gravity = 1
		player:set_physics_override(physics)
	end

	local function set_physics_normal_inv_check(player, extratable)
		if player:get_wielded_item():get_name() == "inventorybags:bag_of_winds_opened" then
			minetest.after(1, set_physics_normal_inv_check, player, extratable)
		else
			set_physics_normal(player)
			minetest.sound_stop(extratable.handle)
			local inv = player:get_inventory()
			local lostbag = false
			while inv:contains_item("main", "inventorybags:bag_of_winds_opened") do
				lostbag = true
				local itemstack = inv:remove_item("main", "inventorybags:bag_of_winds_opened")
				itemstack:set_name("inventorybags:bag_of_winds_closed")
				minetest.item_drop(itemstack, player, player:get_pos())
			end
			while inv:contains_item("craft", "inventorybags:bag_of_winds_opened") do
				lostbag = true
				local itemstack = inv:remove_item("craft", "inventorybags:bag_of_winds_opened")
				itemstack:set_name("inventorybags:bag_of_winds_closed")
				minetest.item_drop(itemstack, player, player:get_pos())
			end
			if lostbag then
				minetest.chat_send_player(player:get_player_name(), "You lost your Bag of Winds.")
			end
		end
	end

	local function set_new_physics(player, itemstack)
		local physics = player:get_physics_override()
		local extratable = {}
		extratable.handle = minetest.sound_play("inventorybags_wind", {gain = 1, object = player, max_hear_distance = 5, loop = true})
		physics.gravity = 1*gravity_change+physics.gravity
		player:set_physics_override(physics)
		minetest.after(1, set_physics_normal_inv_check, player, extratable)
	end

	local function open_bag_of_winds(player, itemstack)
		local meta = itemstack:get_meta()
		set_new_physics(player, itemstack)
		itemstack:set_name("inventorybags:bag_of_winds_opened")
		return itemstack
	end

	local function close_bag_of_winds(player, itemstack)
		set_physics_normal(player)
		itemstack:set_name("inventorybags:bag_of_winds_closed")
		return itemstack
	end

	minetest.register_craftitem("inventorybags:bag_of_winds_closed", {
		description = "Bag of Winds",
		inventory_image = "inventorybags_bag_of_winds_closed.png",
		groups = {bag = 1},
		on_secondary_use = function(itemstack, user, pointed_thing)
			itemstack = open_bag_of_winds(user, itemstack)
			return itemstack
		end,
		on_place = function(itemstack, placer, pointed_thing)
			itemstack = open_bag_of_winds(placer, itemstack)
			return itemstack
		end,
	})

	minetest.register_craftitem("inventorybags:bag_of_winds_opened", {
		description = "Bag of Winds",
		inventory_image = "inventorybags_bag_of_winds_opened.png",
		groups = {bag = 1, not_in_creative_inventory = 1},
		on_secondary_use = function(itemstack, user, pointed_thing)
			itemstack = close_bag_of_winds(user, itemstack)
			return itemstack
		end,
		on_place = function(itemstack, placer, pointed_thing)
			itemstack = close_bag_of_winds(placer, itemstack)
			return itemstack
		end,
		on_drop = function(itemstack, dropper, pos)
			itemstack:set_name("inventorybags:bag_of_winds_closed")
			minetest.item_drop(itemstack, dropper, pos)
			return itemstack
		end
	})
end


code:
-- Craftitem / Misc

minetest.register_craft({
	output = 'inventorybags:item_filter',
	recipe = {
		{'default:steel_ingot', 'group:stick', 'default:steel_ingot'},
		{'group:stick', 'inventorybags:fabric', 'group:stick'},
		{'default:steel_ingot', 'group:stick', 'default:steel_ingot'},
	}
})

minetest.register_craft({
	output = 'inventorybags:item_puller',
	recipe = {
		{'default:steel_ingot', 'default:gold_ingot', 'default:steel_ingot'},
		{'default:obsidian_glass', 'inventorybags:item_filter', 'default:obsidian_glass'},
		{'default:obsidian_glass', '', 'default:obsidian_glass'},
	}
})

minetest.register_craft({
	output = 'inventorybags:inventory_connecter',
	recipe = {
		{'inventorybags:fabric', 'inventorybags:fabric', 'inventorybags:fabric'},
		{'inventorybags:item_puller', 'default:mese_crystal', 'inventorybags:item_puller'},
		{'inventorybags:fabric', 'inventorybags:fabric', 'inventorybags:fabric'},
	}
})

-- Devices

minetest.register_craft({
	output = 'inventorybags:spinning_wheel',
	recipe = {
		{'group:stick', 'group:stick', 'group:wool'},
		{'group:stick', 'group:stick', ''},
		{'group:wood', 'group:wood', 'group:slab'},
	}
})

minetest.register_craft({
	output = 'inventorybags:loom',
	recipe = {
		{'group:stick', 'inventorybags:yarn', 'group:stick'},
		{'group:stick', 'inventorybags:yarn', 'group:stick'},
		{'group:wood', 'group:wood', 'group:wood'},
	}
})

minetest.register_craft({
	output = 'inventorybags:bud',
	recipe = {
		{'group:stick', 'default:meselamp', ''},
		{'group:stick', 'inventorybags:upgrade_base', ''},
		{'group:wood', 'group:wood', 'group:wood'},
	}
})

-- Upgrades

minetest.register_craft({
	output = 'inventorybags:upgrade_base',
	recipe = {
		{'inventorybags:yarn', 'group:stick', 'inventorybags:yarn'},
		{'group:stick', 'default:mese_crystal_fragment', 'group:stick'},
		{'inventorybags:yarn', 'group:stick', 'inventorybags:yarn'},
	}
})

minetest.register_craft({
	type = "shapeless",
	output = "inventorybags:rename_upgrade",
	recipe = {"inventorybags:upgrade_base", "default:paper"},
})

minetest.register_craft({
	type = "shapeless",
	output = "inventorybags:coloring_upgrade",
	recipe = {"inventorybags:upgrade_base", "dye:cyan", "dye:magenta", "dye:yellow", "dye:black", "dye:white"},
})

minetest.register_craft({
	type = "shapeless",
	output = "inventorybags:coloring_upgrade",
	recipe = {"inventorybags:upgrade_base", "dye:red", "dye:green", "dye:blue", "dye:black", "dye:white"},
})

if minetest.get_modpath("node_texture_modifier") then
	minetest.register_craft({
		type = "shapeless",
		output = "inventorybags:coloring_upgrade",
		recipe = {"inventorybags:upgrade_base", "node_texture_modifier:dye_mixture"},
	})
end

minetest.register_craft({
	output = 'inventorybags:collecting_upgrade',
	recipe = {
		{'default:steel_ingot', 'inventorybags:upgrade_base', 'default:steel_ingot'},
		{'default:steel_ingot', 'inventorybags:item_puller', 'default:steel_ingot'},
		{'default:obsidian_glass', '', 'default:obsidian_glass'},
	}
})

minetest.register_craft({
	output = 'inventorybags:advanced_collecting_upgrade',
	recipe = {
		{'default:steel_ingot', 'inventorybags:collecting_upgrade', 'default:steel_ingot'},
		{'default:steel_ingot', 'inventorybags:item_puller', 'default:steel_ingot'},
		{'default:obsidian_glass', '', 'default:obsidian_glass'},
	}
})

minetest.register_craft({
	output = 'inventorybags:sorting_upgrade',
	recipe = {
		{'', 'inventorybags:upgrade_base', ''},
		{'default:ladder_steel', 'default:ladder_steel', 'default:ladder_steel'},
		{'group:wool', 'inventorybags:item_filter', 'group:wool'},
	}
})

minetest.register_craft({
	output = 'inventorybags:autocrafting_upgrade',
	recipe = {
		{'group:wood', 'group:wood', 'group:wood'},
		{'default:steel_ingot', 'default:mese_crystal', 'default:steel_ingot'},
		{'default:steel_ingot', 'inventorybags:upgrade_base', 'default:steel_ingot'},
	}
})

if minetest.get_modpath("pipeworks") then
	minetest.register_craft({
			type = "shapeless",
			output = 'inventorybags:autocrafting_upgrade',
			recipe = {"inventorybags:upgrade_base", "pipeworks:autocrafter",},
		})
end

minetest.register_craft({
	output = 'inventorybags:dumping_upgrade',
	recipe = {
		{'default:steel_ingot', 'inventorybags:upgrade_base', 'default:steel_ingot'},
		{'default:steel_ingot', 'inventorybags:item_puller', 'default:steel_ingot'},
		{'group:stone', 'bucket:bucket_lava', 'group:stone'},
	},
	replacements = {{"bucket:bucket_lava", "bucket:bucket_empty"}},
})

minetest.register_craft({
	output = 'inventorybags:opening_sound_upgrade',
	recipe = {
		{'inventorybags:closing_sound_upgrade'},
	}
})

minetest.register_craft({
	output = 'inventorybags:closing_sound_upgrade',
	recipe = {
		{'inventorybags:opening_sound_upgrade'},
	}
})

minetest.register_craft({
	output = 'inventorybags:opening_sound_upgrade',
	recipe = {
		{'', '', 'default:steel_ingot'},
		{'inventorybags:upgrade_base', 'default:steel_ingot', 'default:paper'},
		{'', '', 'default:steel_ingot'},
	}
})

minetest.register_craft({
	output = 'inventorybags:refilling_upgrade',
	recipe = {
		{'default:bronze_ingot', 'inventorybags:item_puller', 'default:bronze_ingot'},
		{'default:bronze_ingot', 'inventorybags:upgrade_base', 'default:bronze_ingot'},
	}
})

minetest.register_craft({
	output = 'inventorybags:closing_sound_upgrade',
	recipe = {
		{'default:steel_ingot', '', ''},
		{'default:paper', 'default:steel_ingot', 'inventorybags:upgrade_base'},
		{'default:steel_ingot', '', ''},
	}
})

if minetest.registered_items["inventorybags:explosion_upgrade"] then
	minetest.register_craft({
		type = "shapeless",
		output = 'inventorybags:explosion_upgrade',
		recipe = {"inventorybags:upgrade_base", "tnt:gunpowder", "tnt:tnt"},
	})
end

minetest.register_craft({
	output = 'inventorybags:storage_upgrade_tier_1',
	recipe = {
		{'default:steelblock', 'inventorybags:large_pouch', 'default:steelblock'},
		{'inventorybags:large_pouch', 'inventorybags:upgrade_base', 'inventorybags:large_pouch'},
		{'default:steelblock', 'inventorybags:large_pouch', 'default:steelblock'},
	}
})

minetest.register_craft({
	output = 'inventorybags:storage_upgrade_tier_2',
	recipe = {
		{'default:goldblock', 'inventorybags:large_pouch', 'default:goldblock'},
		{'inventorybags:large_pouch', 'inventorybags:storage_upgrade_tier_1', 'inventorybags:large_pouch'},
		{'default:goldblock', 'inventorybags:large_pouch', 'default:goldblock'},
	}
})

minetest.register_craft({
	output = 'inventorybags:storage_upgrade_tier_3',
	recipe = {
		{'default:diamondblock', 'inventorybags:large_pouch', 'default:diamondblock'},
		{'inventorybags:large_pouch', 'inventorybags:storage_upgrade_tier_2', 'inventorybags:large_pouch'},
		{'default:diamondblock', 'inventorybags:large_pouch', 'default:diamondblock'},
	}
})

-- Bags

-- 3 wool
minetest.register_craft({
	output = 'inventorybags:tiny_pouch',
	recipe = {
		{'inventorybags:yarn'},
		{'inventorybags:fabric'},
	},
})

-- 15 wool
minetest.register_craft({
	output = 'inventorybags:small_pouch',
	recipe = {
		{'', 'inventorybags:yarn', ''},
		{'inventorybags:fabric', 'inventorybags:tiny_pouch', 'inventorybags:fabric'},
		{'inventorybags:fabric', 'inventorybags:tiny_pouch', 'inventorybags:fabric'},
	},
})

minetest.register_craft({
	output = 'inventorybags:small_pouch',
	recipe = {
		{'inventorybags:yarn', 'inventorybags:yarn', 'inventorybags:yarn'},
		{'inventorybags:fabric', 'inventorybags:fabric', 'inventorybags:fabric'},
		{'inventorybags:fabric', 'inventorybags:fabric', 'inventorybags:fabric'},
	},
})

-- 39 wool
minetest.register_craft({
	output = 'inventorybags:medium_pouch',
	recipe = {
		{'', 'inventorybags:yarn', ''},
		{'inventorybags:fabric', 'inventorybags:small_pouch', 'inventorybags:fabric'},
		{'inventorybags:fabric', 'inventorybags:small_pouch', 'inventorybags:fabric'},
	},
})

-- 87 wool
minetest.register_craft({
	output = 'inventorybags:large_pouch',
	recipe = {
		{'', 'inventorybags:yarn', ''},
		{'inventorybags:fabric', 'inventorybags:medium_pouch', 'inventorybags:fabric'},
		{'inventorybags:fabric', 'inventorybags:medium_pouch', 'inventorybags:fabric'},
	},
})

minetest.register_craft({
	output = 'inventorybags:backpack',
	recipe = {
		{'inventorybags:fabric', 'inventorybags:fabric', 'inventorybags:yarn'},
		{'inventorybags:fabric', 'inventorybags:large_pouch', 'inventorybags:yarn'},
		{'inventorybags:fabric', 'inventorybags:large_pouch', 'inventorybags:yarn'},
	},
})

minetest.register_craft({
	output = 'inventorybags:suitcase',
	recipe = {
		{'default:steel_ingot', 'default:steel_ingot', 'default:steel_ingot'},
		{'inventorybags:large_pouch', 'inventorybags:fabric', 'inventorybags:large_pouch'},
		{'default:steel_ingot', 'default:steel_ingot', 'default:steel_ingot'},
	},
})

minetest.register_craft({
	output = 'inventorybags:bag_on_a_stick',
	recipe = {
		{'', '', 'group:stick'},
		{'', 'group:stick', 'inventorybags:large_pouch'},
		{'group:stick', '', 'inventorybags:large_pouch'},
	},
})

minetest.register_craft({
	output = 'inventorybags:belt_bag',
	recipe = {
		{'inventorybags:yarn', 'inventorybags:large_pouch', 'inventorybags:yarn'},
		{'inventorybags:yarn', 'inventorybags:fabric', 'inventorybags:yarn'},
		{'inventorybags:yarn', 'inventorybags:large_pouch', 'inventorybags:yarn'},
	},
})

minetest.register_craft({
	output = 'inventorybags:hand_bag',
	recipe = {
		{'inventorybags:yarn', '', 'inventorybags:yarn'},
		{'inventorybags:fabric', 'inventorybags:large_pouch', 'inventorybags:fabric'},
		{'inventorybags:fabric', 'inventorybags:large_pouch', 'inventorybags:fabric'},
	},
})

minetest.register_craft({
	output = 'inventorybags:chest_bag',
	recipe = {
		{'inventorybags:yarn', '', 'inventorybags:yarn'},
		{'group:wood', 'group:wood', 'group:wood'},
		{'inventorybags:large_pouch', 'default:chest', 'inventorybags:large_pouch'},
	},
})

minetest.register_craft({
	output = 'inventorybags:satchel',
	recipe = {
		{'inventorybags:yarn', 'inventorybags:yarn', 'inventorybags:yarn'},
		{'inventorybags:yarn', '', 'inventorybags:yarn'},
		{'inventorybags:large_pouch', 'inventorybags:fabric', 'inventorybags:large_pouch'},
	},
})

minetest.register_craft({
	output = 'inventorybags:paper_bag',
	recipe = {
		{'default:paper', '', 'default:paper'},
		{'default:paper', 'default:paper', 'default:paper'},
		{'default:paper', 'default:paper', 'default:paper'},
	},
})

if minetest.registered_items["homedecor:plastic_sheeting"] then
	minetest.register_craft({
		output = 'inventorybags:plastic_bag',
		recipe = {
			{'homedecor:plastic_sheeting', '', 'homedecor:plastic_sheeting'},
			{'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting'},
			{'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting'},
		},
	})
end

-- 395 wool
minetest.register_craft({
	output = 'inventorybags:bag_transporting_bag',
	recipe = {
		{'inventorybags:fabric', 'inventorybags:large_pouch', 'inventorybags:fabric'},
		{'inventorybags:large_pouch', 'inventorybags:small_pouch', 'inventorybags:large_pouch'},
		{'inventorybags:fabric', 'inventorybags:large_pouch', 'inventorybags:fabric'},
	},
})

if minetest.setting_getbool("inventorybags_enable_item_teleportation_bag") then
	minetest.register_craft({
		output = 'inventorybags:item_teleportation_bag',
		recipe = {
			{'default:diamond', 'inventorybags:large_pouch', 'default:diamond'},
			{'default:diamond', 'inventorybags:inventory_connecter', 'default:diamond'},
			{'default:diamond', 'default:mese', 'default:diamond'},
		},
	})
	if minetest.get_modpath("pipeworks") then
		minetest.register_craft({
			type = "shapeless",
			output = 'inventorybags:item_teleportation_bag',
			recipe = {"inventorybags:large_pouch", "inventorybags:inventory_connecter", "pipeworks:teleport_tube_1"},
		})
	end
end

if minetest.get_modpath("xdecor") then
	minetest.register_craft({
		type = "shapeless",
		output = 'inventorybags:ender_bag',
		recipe = {"inventorybags:large_pouch", "inventorybags:inventory_connecter", "xdecor:enderchest"},
	})
end

if minetest.get_modpath("more_chests") then
	minetest.register_craft({
		type = "shapeless",
		output = 'inventorybags:wifi_bag',
		recipe = {"inventorybags:large_pouch","inventorybags:inventory_connecter", "more_chests:wifi"},
	})
end

if minetest.get_modpath("beds") then
	minetest.register_craft({
		output = 'inventorybags:sleeping_bag',
		recipe = {
			{"wool:red", "wool:red", "wool:white"},
			{"", "inventorybags:large_pouch", ""}
		},
	})

	minetest.register_craft({
		type = "shapeless",
		output = 'inventorybags:sleeping_bag',
		recipe = {"inventorybags:large_pouch", "group:bed"},
	})
end
if not minetest.setting_getbool("inventorybags_dialable_bag_of_winds") then
	minetest.register_craft({
		output = 'inventorybags:bag_of_winds_closed',
		recipe = {
			{"default:goldblock", "inventorybags:large_pouch", "default:goldblock"},
			{"default:diamondblock", "default:mese", "default:diamondblock"},
			{"default:diamondblock", "default:mese", "default:diamondblock"}
		},
	})
end

repo_name: Desour/PlayMinetestServer

license: mit


code:
minetest.register_craftitem("inventorybags:yarn", {
	description = "Yarn",
	inventory_image = "inventorybags_yarn.png",
})

minetest.register_craftitem("inventorybags:fabric", {
	description = "Fabric",
	inventory_image = "inventorybags_fabric.png",
})

minetest.register_craftitem("inventorybags:upgrade_base", {
	description = "Upgrade Base",
	inventory_image = "inventorybags_upgrade_base.png",
	groups = {inventorybags_upgrade = 1}
})

minetest.register_craftitem("inventorybags:item_filter", {
	description = "Item Filter",
	inventory_image = "inventorybags_item_filter.png",
})

minetest.register_craftitem("inventorybags:item_puller", {
	description = "Item Puller",
	inventory_image = "inventorybags_item_puller.png",
})

minetest.register_craftitem("inventorybags:inventory_connecter", {
	description = "Inventory Connecter",
	inventory_image = "inventorybags_inventory_connecter.png",
})


code:
default
wool
farming
stairs
dye
bucket
unified_inventory?
more_chests?
xdecor?
beds?
pipeworks?
homedecor?
node_texture_modifier?
tnt?


code: A mod which adds bags.


code:
local function drop_inv(inv, player, plistname)
	if plistname then
		local listname = plistname
		local size = inv:get_size(listname)
		if size then
			for i = 1, size, 1 do
				local stack = inv:get_stack(listname, i)
				if stack:get_name() ~="" then
					minetest.item_drop(stack, player, player:get_pos())
					inv:set_stack(listname, i, "")
				end
			end
		end
	else
		for listname, list in pairs(inv:get_lists()) do
			local size = inv:get_size(listname)
			if size then
				for i = 1, size, 1 do
					local stack = inv:get_stack(listname, i)
					if stack:get_name() ~="" then
						minetest.item_drop(stack, player, player:get_pos())
						inv:set_stack(listname, i, "")
					end
				end
			end
		end
	end
end

local function rbg_to_hex(red, green, blue, alpha)
	if((red < 0 or red > 255 or green < 0 or green > 255 or blue < 0 or blue > 255) or
			(alpha and (alpha < 0 or alpha > 255))) then
		return nil
	end
	if(alpha) then
		return string.format("#%.2X%.2X%.2X%.2X", red,green,blue,alpha)
	else
		return string.format("#%.2X%.2X%.2X", red,green,blue)
	end
end

local function hex_to_rbg(hex)
    hex = hex:gsub("#","")
    return tonumber("0x"..hex:sub(1,2)), tonumber("0x"..hex:sub(3,4)), tonumber("0x"..hex:sub(5,6))
end

local function open_spinning_wheel(user)
	local playername = user:get_player_name()
	local invname = "inventorybags_"..playername.."_spinning_wheel"
	local inv = minetest.get_inventory({type="player", name=playername})
	inv:set_size(invname.."_in", 1)
	inv:set_size(invname.."_out", 4)

	minetest.show_formspec(playername, invname,
		"size[8,7]" ..
		default.gui_bg ..
		default.gui_bg_img ..
		default.gui_slots ..
		"list[current_player;"..invname.."_in;1,0.8;1,1;]" ..
		"item_image[1,0.8;1,1;wool:white]" ..
		"button[3,0.8;1,1;spin;Spin]" ..
		"list[current_player;"..invname.."_out;5,0.3;2,2;]" ..
		"list[current_player;main;0,2.85;8,1;]" ..
		"list[current_player;main;0,4.08;8,3;8]" ..
		"listring[current_player;"..invname.."_out]" ..
		"listring[current_player;main]" ..
		"listring[current_player;"..invname.."_in]" ..
		"listring[current_player;main]" ..
		default.get_hotbar_bg(0,2.85)
	)
end

minetest.register_craftitem("inventorybags:spinning_wheel", {
	description = "Spinning Wheel (right click to use)",
	inventory_image = "inventorybags_spinning_wheel.png",
	on_secondary_use = function(itemstack, user)
		return open_spinning_wheel(user)
	end,
	on_place = function(itemstack, placer, pointed_thing)
		return open_spinning_wheel(placer)
	end,
})

local function open_loom(user)
	local playername = user:get_player_name()
	local invname = "inventorybags_"..playername.."_loom"
	local inv = minetest.get_inventory({type="player", name=playername})
	inv:set_size(invname.."_in", 2)
	inv:set_size(invname.."_out", 4)

	minetest.show_formspec(playername, invname,
		"size[8,7]" ..
		default.gui_bg ..
		default.gui_bg_img ..
		default.gui_slots ..
		"list[current_player;"..invname.."_in;1,0.3;1,2;]" ..
		"item_image[1,0.3;1,1;inventorybags:yarn]" ..
		"item_image[1,1.3;1,1;inventorybags:yarn]" ..
		"button[3,0.8;1,1;weave;Weave]" ..
		"list[current_player;"..invname.."_out;5,0.3;2,2;]" ..
		"list[current_player;main;0,2.85;8,1;]" ..
		"list[current_player;main;0,4.08;8,3;8]" ..
		"listring[current_player;"..invname.."_out]" ..
		"listring[current_player;main]" ..
		"listring[current_player;"..invname.."_in]" ..
		"listring[current_player;main]" ..
		default.get_hotbar_bg(0,2.85)
	)
end

minetest.register_craftitem("inventorybags:loom", {
	description = "Loom (right click to use)",
	inventory_image = "inventorybags_loom.png",
	on_secondary_use = function(itemstack, user)
		return open_loom(user)
	end,
	on_place = function(itemstack, placer, pointed_thing)
		return open_loom(placer)
	end,
})

local function open_bud(user)
	local playername = user:get_player_name()
	local invname = "inventorybags_"..playername.."_bud"
	local inv = minetest.get_inventory({type="player", name=playername})
	local setting_default = minetest.formspec_escape(user:get_attribute("inventorybags_setting_default")) or ""
	inv:set_size(invname.."_bag", 1)
	inv:set_size(invname.."_upgrade", 1)
	inv:set_size(invname.."_out", 1)
	minetest.show_formspec(playername, invname,
		"size[8,7]" ..
		default.gui_bg ..
		default.gui_bg_img ..
		default.gui_slots ..
		"field[0.4,2.2;3.2,0;setting;Setting;"..setting_default.."]" ..
		"field_close_on_enter[setting;false]" ..
		"item_image[0.5,0.3;1,1;inventorybags:large_pouch]" ..
		"item_image[2,0.3;1,1;inventorybags:upgrade_base]" ..
		"button[3.5,0.3;1,1;upgrade;Upgrade]" ..
		"button[4.5,0.3;1.8,1;extract;Extract Upgrade]" ..
		"button[3.5,1.4;1,1;help;Help]" ..
		"button[4.5,1.4;1.8,1;setting_creator;Setting Creator]" ..
		"button[6.3,1.4;1.8,1;recover_setting;Recover Setting]" ..
		"list[current_player;"..invname.."_bag;0.5,0.3;1,1;]" ..
		"list[current_player;"..invname.."_upgrade;2,0.3;1,1;]" ..
		"list[current_player;"..invname.."_out;6.5,0.3;1,1;]" ..
		"list[current_player;main;0,2.85;8,1;]" ..
		"list[current_player;main;0,4.08;8,3;8]" ..
		"listring[current_player;"..invname.."_out]" ..
		"listring[current_player;main]" ..
		"listring[current_player;"..invname.."_bag]" ..
		"listring[current_player;main]" ..
		"listring[current_player;"..invname.."_upgrade]" ..
		"listring[current_player;main]" ..
		default.get_hotbar_bg(0,2.85)
	)
end

minetest.register_craftitem("inventorybags:bud", {
	description = "Bag Upgrade Device (right click to use)",
	inventory_image = "inventorybags_bud.png",
	on_secondary_use = function(itemstack, user)
		return open_bud(user)
	end,
	on_place = function(itemstack, placer, pointed_thing)
		return open_bud(placer)
	end,
})

-- too lazy to make an api for one recipe :P
--inventorybags.spinning_wheel_recipes = {}
--inventorybags.loom_recipes = {}
if minetest.get_modpath("unified_inventory") then
	unified_inventory.register_craft_type("spinning", {
		description = "spinning",
		width = 1,
		height = 1,
	})
	unified_inventory.register_craft({
		type = "spinning",
		items = {"group:wool"},
		output = "inventorybags:yarn",
		width = 0,
	})
	unified_inventory.register_craft_type("weaving", {
		description = "weaving",
		width = 1,
		height = 2,
	})
	unified_inventory.register_craft({
		type = "weaving",
		items = {"inventorybags:yarn", "inventorybags:yarn"},
		output = "inventorybags:fabric",
		width = 0,
	})
end

local function set_crafting_output(inv)
	local input = {}
	input.method, input.width, input.items = "normal", 3, {}
	for i = 1, inv:get_size("recipe"), 1 do
		input.items[i] = inv:get_stack("recipe", i)
	end
	inv:set_stack("output", 1, minetest.get_craft_result(input).item)
end

local function show_setting_creator(player, setting_type, extra_table)
	local playername = player:get_player_name()
	local invname = "inventorybags_"..playername.."_bud_setting_creator"
	local inv = minetest.get_inventory({type="player", name=playername})
	local setting = player:get_attribute("inventorybags_setting_default")
	local setting_default = minetest.formspec_escape(setting) or ""
	if not extra_table then
		extra_table = {}
	end
	if setting_type == "color" then
		extra_table.preview = extra_table.preview or "black"
		extra_table.color_index = extra_table.color_index or "1"
		extra_table.red = extra_table.red or "0"
		extra_table.green = extra_table.green or "0"
		extra_table.blue = extra_table.blue or "0"
		extra_table.alpha = extra_table.alpha or "255"
		local colorss = "Named Colors:"
		for _, colort in pairs(inventorybags.named_colors) do
			colorss = colorss..","..colort.color..colort.name
		end
		local color_list = "textlist[0,0;7.5,2;color_list;"..colorss..";"..extra_table.color_index..";false]"
		minetest.show_formspec(playername, invname.."_color",
			"size[8,9]" ..
			default.gui_bg ..
			default.gui_bg_img ..
			default.gui_slots ..
			"field[0.2,4.3;4.3,0;setting;Setting;"..setting_default.."]" ..
			"field_close_on_enter[setting;false]" ..
			"field[0.2,3.1;1.4,0;red;Red (0-255);"..extra_table.red.."]" ..
			"field_close_on_enter[red;false]" ..
			"field[1.6,3.1;1.4,0;green;Green (0-255);"..extra_table.green.."]" ..
			"field_close_on_enter[green;false]" ..
			"field[3.0,3.1;1.4,0;blue;Blue (0-255);"..extra_table.blue.."]" ..
			"field_close_on_enter[blue;false]" ..
			color_list ..
			"label[4.5,3.2;Preview:]"..
			"box[4.5,3.6;0.8,0.8;"..extra_table.preview.."]" ..
			"button[6,3.5;2,1;back;Back and Save]" ..
			"button[4.2,2.4;1.6,1;set_color;Set Color]" ..
			"button[6,2.4;2,1;random_color;Random Color]" ..
			"list[current_player;main;0,4.85;8,1;]" ..
			"list[current_player;main;0,6.08;8,3;8]" ..
			default.get_hotbar_bg(0,4.85)
		)
	elseif setting_type == "filter" then
		local finv = minetest.create_detached_inventory(invname.."_filter", {

			allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
				inv:set_stack(to_list, to_index, inv:get_stack(from_list, from_index))
				inv:set_stack(from_list, from_index, "")
				return 0
			end,
			allow_put = function(inv, listname, index, stack, player)
				inv:set_stack(listname, index, stack:get_name())
				return 0
			end,
			allow_take = function(inv, listname, index, stack, player)
				inv:set_stack(listname, index, "")
				return 0
			end,
		}, playername)
		finv:set_size("whitelist", 4*1)
		finv:set_size("blacklist", 4*1)
		local setting_table = minetest.deserialize(setting) or {}
		local group_filter_whitelist_default = setting_table.group_filter_whitelist or ""
		local mod_filter_whitelist_default = setting_table.mod_filter_whitelist or ""
		local name_filter_whitelist_default = setting_table.name_filter_whitelist or ""
		local group_filter_blacklist_default = setting_table.group_filter_blacklist or ""
		local mod_filter_blacklist_default = setting_table.mod_filter_blacklist or ""
		local name_filter_blacklist_default = setting_table.name_filter_whitelist or ""
		if setting_table.whitelist_items and setting_table.whitelist_counter then
			for i, stack in pairs(setting_table.whitelist_items) do
				finv:set_stack("whitelist", i-setting_table.whitelist_counter+1, stack)
			end
		end
		if setting_table.blacklist_items and setting_table.blacklist_counter then
			for i, stack in pairs(setting_table.blacklist_items) do
				finv:set_stack("blacklist", i-setting_table.blacklist_counter+1, stack)
			end
		end
		minetest.show_formspec(playername, invname.."_filter",
			"size[12,11]" ..
			default.gui_bg ..
			default.gui_bg_img ..
			default.gui_slots ..
			"label[0.2,0.1;Whitelist:          (An empty whitelist doesn't block any item)]"..
			"label[6.2,0.1;Blacklist:]"..
			"box[0,0;5.8,5.4;white]" ..
			"box[6,0;5.8,5.4;black]" ..
			"box[0.1,0.1;5.6,5.2;black]" ..
			"box[6.1,0.1;5.6,5.2;#6B6B6B]" ..
			"label[0.2,0.4;"..minetest.formspec_escape('comma separated lists for example: "moreblocks,default,technic"').."]"..
			"label[6.2,0.4;"..minetest.formspec_escape('comma separated lists for example: "tree,stone,sand,stick"').."]"..
			"field[0.5,1.3;5.6,1;group_filter_whitelist;Group Filter:;"..group_filter_whitelist_default.."]" ..
			"field[0.5,2.3;5.6,1;mod_filter_whitelist;Mod Filter:;"..mod_filter_whitelist_default.."]" ..
			"field[0.5,3.3;5.6,1;name_filter_whitelist;Name Filter:;"..name_filter_whitelist_default.."]" ..
			"field[6.5,1.3;5.6,1;group_filter_blacklist;Group Filter:;"..group_filter_blacklist_default.."]" ..
			"field[6.5,2.3;5.6,1;mod_filter_blacklist;Mod Filter:;"..mod_filter_blacklist_default.."]" ..
			"field[6.5,3.3;5.6,1;name_filter_blacklist;Name Filter:;"..name_filter_blacklist_default.."]" ..
			"field_close_on_enter[group_filter_whitelist;false]" ..
			"field_close_on_enter[mod_filter_whitelist;false]" ..
			"field_close_on_enter[name_filter_whitelist;false]" ..
			"field_close_on_enter[group_filter_blacklist;false]" ..
			"field_close_on_enter[mod_filter_blacklist;false]" ..
			"field_close_on_enter[name_filter_blacklist;false]" ..
			"label[0.24,3.8;Item Filter:]"..
			"label[6.24,3.8;Item Filter:]"..
			"list[detached:"..invname.."_filter;whitelist;1,4.3;4,1;]" ..
			"list[detached:"..invname.."_filter;blacklist;7,4.3;4,1;]" ..
			"listring[detached:"..invname.."_filter;blacklist]" ..
			"listring[current_player;main]" ..
			"listring[current_player;nothing]" ..
			"listring[detached:"..invname.."_filter;whitelist]" ..
			"listring[current_player;main]" ..
			"listring[current_player;nothing]" ..
			"button[0.2,4.3;0.8,1;back_whitelist;<--]" ..
			"button[5,4.3;0.8,1;next_whitelist;-->]" ..
			"button[6.2,4.3;0.8,1;back_blacklist;<--]" ..
			"button[11,4.3;0.8,1;next_blacklist;-->]" ..
			"field[2.2,6.3;6,0;setting;Setting;"..setting_default.."]" ..
			"field_close_on_enter[setting;false]" ..
			"button[8,5.5;2,1;back;Back and Save]" ..
			"list[current_player;main;2,6.85;8,1;]" ..
			"list[current_player;main;2,8.08;8,3;8]" ..
			default.get_hotbar_bg(2,6.85)
		)
	elseif setting_type == "crafting" then
		local craftinv = minetest.create_detached_inventory(invname.."_crafting", {
			allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
				inv:set_stack(to_list, to_index, inv:get_stack(from_list, from_index))
				inv:set_stack(from_list, from_index, "")
				if from_list == "output" then
					inv:set_list("recipe", {})
				end
				set_crafting_output(inv)
				return 0
			end,
			allow_put = function(inv, listname, index, stack, player)
				inv:set_stack(listname, index, stack:get_name())
				set_crafting_output(inv)
				return 0
			end,
			allow_take = function(inv, listname, index, stack, player)
				inv:set_stack(listname, index, "")
				if listname == "output" then
					inv:set_list("recipe", {})
				end
				set_crafting_output(inv)
				return 0
			end,
		}, playername)
		craftinv:set_size("recipe", 3*3)
		craftinv:set_size("output", 1*1)
		local setting_table = minetest.deserialize(setting) or {}
		if setting_table.recipe_counter and setting_table.recipe_items[setting_table.recipe_counter] then
			for i, stack in pairs(setting_table.recipe_items[setting_table.recipe_counter]) do
				craftinv:set_stack("recipe", i, stack)
			end
		end
		set_crafting_output(craftinv)
		minetest.show_formspec(playername, invname.."_crafting",
			"size[8,9]" ..
			default.gui_bg ..
			default.gui_bg_img ..
			default.gui_slots ..
			"field[0.2,4.3;6,0;setting;Setting;"..setting_default.."]" ..
			"field_close_on_enter[setting;false]" ..
			"button[6,3.5;2,1;back;Back and Save]" ..
			"button[0,0;1,4;back_craft;<--]" ..
			"button[7,0;1,4;next_craft;-->]" ..
			"listring[detached:"..invname.."_crafting;recipe]" ..
			"listring[current_player;main]" ..
			"listring[current_player;nothing]" ..
			"listring[detached:"..invname.."_crafting;output]" ..
			"listring[current_player;main]" ..
			"listring[current_player;nothing]" ..
			"image[4.5,1.3;1,1;gui_furnace_arrow_bg.png^[transformR270]" ..
			"list[detached:"..invname.."_crafting;recipe;1.5,0.3;3,3;]" ..
			"list[detached:"..invname.."_crafting;output;5.5,1.3;1,1;]" ..
			"list[current_player;main;0,4.85;8,1;]" ..
			"list[current_player;main;0,6.08;8,3;8]" ..
			default.get_hotbar_bg(0,4.85)
		)
	end
end

local function save_filter_formspec(fields, setting_table, player, finv, extra_table)
	extra_table = extra_table or {}

	setting_table.group_filter_whitelist = fields.group_filter_whitelist or ""
	setting_table.mod_filter_whitelist = fields.mod_filter_whitelist or ""
	setting_table.name_filter_whitelist = fields.name_filter_whitelist or ""
	setting_table.group_filter_blacklist = fields.group_filter_blacklist or ""
	setting_table.mod_filter_blacklist = fields.mod_filter_blacklist or ""
	setting_table.name_filter_blacklist = fields.name_filter_blacklist or ""

	local wcc = extra_table.whitelist_counter_change or 0
	local bcc = extra_table.blacklist_counter_change or 0
	for i = 1, finv:get_size("whitelist"), 1 do
		local stack_name = finv:get_stack("whitelist", i):get_name()
		if stack_name ~= "" then
			setting_table.whitelist_items[i+setting_table.whitelist_counter-1-wcc] = stack_name
		else
			setting_table.whitelist_items[i+setting_table.whitelist_counter-1-wcc] = nil
		end
	end
	for i = 1, finv:get_size("blacklist"), 1 do
		local stack_name = finv:get_stack("blacklist", i):get_name()
		if stack_name ~= "" then
			setting_table.blacklist_items[i+setting_table.blacklist_counter-1-bcc] = stack_name
		else
			setting_table.blacklist_items[i+setting_table.blacklist_counter-1-bcc] = nil
		end
	end
	player:set_attribute("inventorybags_setting_default", minetest.serialize(setting_table))
end

local function save_crafting_formspec(fields, setting_table, player, craftinv, extra_table)
	extra_table = extra_table or {}
	local rcc = extra_table.recipe_counter_change or 0
	local tableindex = setting_table.recipe_counter-rcc
	if not setting_table.recipe_items[tableindex] then
		setting_table.recipe_items[tableindex] = {}
	end
	for i = 1, craftinv:get_size("recipe"), 1 do
		local stack_name = craftinv:get_stack("recipe", i):get_name()
		if stack_name ~= "" then
			setting_table.recipe_items[tableindex][i] = stack_name
		else
			setting_table.recipe_items[tableindex][i] = nil
		end
	end
	player:set_attribute("inventorybags_setting_default", minetest.serialize(setting_table))
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
	local playername = player:get_player_name()
	local inv = player:get_inventory()

	local spininvname = "inventorybags_"..playername.."_spinning_wheel"
	local loominvname = "inventorybags_"..playername.."_loom"
	local budinvname = "inventorybags_"..playername.."_bud"
	if formname == spininvname then
		if fields.spin then
			minetest.sound_play("inventorybags_spinning_wheel", {gain = 0.8, object = player, max_hear_distance = 5})
			while minetest.get_item_group(inv:get_stack(spininvname.."_in", 1):get_name(), "wool") > 0 and
					inv:room_for_item(spininvname.."_out", "inventorybags:yarn") do
				inv:remove_item(spininvname.."_in", inv:get_stack(spininvname.."_in", 1):get_name())
				inv:add_item(spininvname.."_out", "inventorybags:yarn")
			end
		end
		if fields.quit then
			drop_inv(inv, player, spininvname.."_in")
			drop_inv(inv, player, spininvname.."_out")
		end
	elseif formname == loominvname then
		if fields.weave then
			minetest.sound_play("inventorybags_loom", {gain = 0.5, object = player, max_hear_distance = 5})
			while inv:get_stack(loominvname.."_in", 1):get_name() == "inventorybags:yarn" and
					inv:get_stack(loominvname.."_in", 2):get_name() == "inventorybags:yarn" and
					inv:room_for_item(loominvname.."_out", "inventorybags:fabric") do
				local stack1 = inv:get_stack(loominvname.."_in", 1)
				local stack2 = inv:get_stack(loominvname.."_in", 2)
				stack1:set_count(stack1:get_count()-1)
				stack2:set_count(stack2:get_count()-1)
				inv:set_stack(loominvname.."_in", 1, stack1)
				inv:set_stack(loominvname.."_in", 2, stack2)
				inv:add_item(loominvname.."_out", "inventorybags:fabric")
			end
		end
		if fields.quit then
			drop_inv(inv, player, loominvname.."_in")
			drop_inv(inv, player, loominvname.."_out")
		end
	elseif formname == budinvname then
		local bagstack = inv:get_stack(budinvname.."_bag", 1)
		local upgradestack = inv:get_stack(budinvname.."_upgrade", 1)
		if fields.setting_creator then
			local bud_recipe = inventorybags.bud_recipes[upgradestack:get_name()]
			if bud_recipe then
				if fields.setting then
					player:set_attribute("inventorybags_setting_default", fields.setting)
				end
				show_setting_creator(player,  bud_recipe.setting_type)
			end
		elseif fields.recover_setting then
			if bagstack ~= nil then
				local meta = bagstack:get_meta()
				local upgrade_history = meta:get_string("inventorybags_upgrade_history")
				local cpos = string.find(upgrade_history, ",")
				if cpos then
					local upgrade_name = string.sub(upgrade_history, 1, cpos-1)
					local bud_recipe = inventorybags.bud_recipes[upgrade_name]
					if bud_recipe and bud_recipe.setting_meta_name then
						player:set_attribute("inventorybags_setting_default", meta:get_string(bud_recipe.setting_meta_name))
						open_bud(player)
					end
				end
			end
		elseif fields.help then
			if fields.setting then
				player:set_attribute("inventorybags_setting_default", fields.setting)
			end
			minetest.show_formspec(playername, formname.."_help",
				"size[10,8]" ..
				default.gui_bg ..
				default.gui_bg_img ..
				default.gui_slots ..
				"textlist[0,0;10,8;helptext;".. inventorybags.upgrade_help_string ..";1;true]"
			)
		elseif fields.upgrade then
			if inv:is_empty(budinvname.."_out") and bagstack ~= nil and upgradestack ~= nil and
					bagstack:get_count() == 1 and
					minetest.get_item_group(bagstack:get_name(), "bag") > 0 and
					minetest.get_item_group(upgradestack:get_name(), "inventorybags_upgrade") > 0 then
				local meta = bagstack:get_meta()
				local bud_recipe = inventorybags.bud_recipes[upgradestack:get_name()]
				if bud_recipe and meta:get_string(bud_recipe.meta_name) ~= bud_recipe.meta_string then
					meta:set_string(bud_recipe.meta_name, bud_recipe.meta_string)
					meta:set_string("inventorybags_upgrade_history", upgradestack:get_name()..","..meta:get_string("inventorybags_upgrade_history"))
					if bud_recipe.setting_meta_name then
						meta:set_string(bud_recipe.setting_meta_name, fields.setting)
					end
					minetest.sound_play("inventorybags_bud_upgrade", {gain = 0.8, object = player, max_hear_distance = 5})
					upgradestack:set_count(upgradestack:get_count()-1)
					inv:set_stack(budinvname.."_bag", 1, "")
					inv:set_stack(budinvname.."_upgrade", 1, upgradestack)
					inv:set_stack(budinvname.."_out", 1, bagstack)
				end
			end
		elseif fields.extract then
			if inv:is_empty(budinvname.."_out") and
					bagstack ~= nil and
					minetest.get_item_group(bagstack:get_name(), "bag") > 0	and
					bagstack:get_count() == 1 then
				local meta = bagstack:get_meta()
				local upgrade_history = meta:get_string("inventorybags_upgrade_history")
				local cpos = string.find(upgrade_history, ",")
				if cpos then
					local upgrade_name = string.sub(upgrade_history, 1, cpos-1)
					local bud_recipe = inventorybags.bud_recipes[upgrade_name]
					if bud_recipe and inv:room_for_item(budinvname.."_upgrade", upgrade_name) then
						meta:set_string("inventorybags_upgrade_history", string.sub(upgrade_history, cpos+1))
						meta:set_string(bud_recipe.meta_name, "")
						if bud_recipe.setting_meta_name then
							meta:set_string(bud_recipe.setting_meta_name, "")
						end
						minetest.sound_play("inventorybags_bud_extract", {gain = 0.8, object = player, max_hear_distance = 5})
						inv:add_item(budinvname.."_upgrade", upgrade_name)
						inv:set_stack(budinvname.."_bag", 1, "")
						inv:set_stack(budinvname.."_out", 1, bagstack)
					end
				end
			end
		end
		if fields.quit then
			player:set_attribute("inventorybags_setting_default", "")
			drop_inv(inv, player, budinvname.."_bag")
			drop_inv(inv, player, budinvname.."_upgrade")
			drop_inv(inv, player, budinvname.."_out")
		end
	elseif formname == budinvname.."_help" then
		if fields.quit then
			minetest.after(0.1, open_bud, player)
		end
	elseif formname == budinvname.."_setting_creator_color" then
		if fields.color_list then
			local fieldstable = minetest.explode_textlist_event(fields.color_list)
			if fieldstable.type == "CHG" then
				if fieldstable.index ~= 1 then
					player:set_attribute("inventorybags_setting_default", inventorybags.named_colors[fieldstable.index-1].name)
					local r, g, b = hex_to_rbg(inventorybags.named_colors[fieldstable.index-1].color)
					show_setting_creator(player, "color", {color_index = fieldstable.index, red = r, green = g,
						blue = b, preview = inventorybags.named_colors[fieldstable.index-1].name})
				end
			end
		elseif fields.set_color then
			local r, g, b = tonumber(fields.red) or 0, tonumber(fields.green) or 0, tonumber(fields.blue) or 0
			if r > 255 then r = 255 end
			if g > 255 then g = 255 end
			if b > 255 then b = 255 end
			local colorhex = rbg_to_hex(r, g, b)
			if colorhex then
				player:set_attribute("inventorybags_setting_default", colorhex)
				show_setting_creator(player, "color", {red = r, green = g, blue = b, alpha = a, preview = colorhex})
			end
		elseif fields.random_color then
			local r, g, b = math.random(0, 255), math.random(0, 255), math.random(0, 255)
			local colorhex = rbg_to_hex(r, g, b)
			player:set_attribute("inventorybags_setting_default", colorhex)
			show_setting_creator(player, "color", {red = r, green = g, blue = b, alpha = 255, preview = colorhex})
		elseif fields.back then
			if fields.setting then
				player:set_attribute("inventorybags_setting_default", fields.setting)
			end
			open_bud(player)
		end
		if fields.quit then
			minetest.after(0.1, open_bud, player)
		end
	elseif formname == budinvname.."_setting_creator_filter" then
		local finv = minetest.get_inventory({type="detached", name=budinvname.."_setting_creator_filter"})
		local setting_table = {}
		if fields.setting then
			setting_table = minetest.deserialize(fields.setting) or {}
		end
		setting_table.whitelist_counter = setting_table.whitelist_counter or 1
		setting_table.blacklist_counter = setting_table.blacklist_counter or 1
		setting_table.blacklist_items = setting_table.blacklist_items or {}
		setting_table.whitelist_items = setting_table.whitelist_items or {}

		if fields.back_whitelist then
			if setting_table.whitelist_counter > 1 then
				setting_table.whitelist_counter = setting_table.whitelist_counter-1
				save_filter_formspec(fields, setting_table, player, finv, {whitelist_counter_change = -1})
			else
				save_filter_formspec(fields, setting_table, player, finv)
			end
			show_setting_creator(player, "filter", {})
		elseif fields.next_whitelist then
			setting_table.whitelist_counter = setting_table.whitelist_counter+1
			save_filter_formspec(fields, setting_table, player, finv, {whitelist_counter_change = 1})
			show_setting_creator(player, "filter", {})
		elseif fields.back_blacklist then
			if setting_table.blacklist_counter > 1 then
				setting_table.blacklist_counter = setting_table.blacklist_counter-1
				save_filter_formspec(fields, setting_table, player, finv, {blacklist_counter_change = -1})
			else
				save_filter_formspec(fields, setting_table, player, finv)
			end
			show_setting_creator(player, "filter", {})
		elseif fields.next_blacklist then
			setting_table.blacklist_counter = setting_table.blacklist_counter+1
			save_filter_formspec(fields, setting_table, player, finv, {blacklist_counter_change = 1})
			show_setting_creator(player, "filter", {})
		elseif fields.back then
			save_filter_formspec(fields, setting_table, player, finv)
			open_bud(player)
		end
		if fields.quit then
			minetest.after(0.1, open_bud, player)
		end
	elseif formname == budinvname.."_setting_creator_crafting" then
		local craftinv = minetest.get_inventory({type="detached", name=budinvname.."_setting_creator_crafting"})
		local setting_table = {}
		if fields.setting then
			setting_table = minetest.deserialize(fields.setting) or {}
		end
		setting_table.recipe_counter = setting_table.recipe_counter or 1
		setting_table.recipe_items = setting_table.recipe_items or {}
		if fields.back_craft then
			if setting_table.recipe_counter > 1 then
				setting_table.recipe_counter = setting_table.recipe_counter-1
				save_crafting_formspec(fields, setting_table, player, craftinv, {recipe_counter_change = -1})
			else
				save_crafting_formspec(fields, setting_table, player, craftinv)
			end
			show_setting_creator(player, "crafting", {})
		elseif fields.next_craft then
			setting_table.recipe_counter = setting_table.recipe_counter+1
			save_crafting_formspec(fields, setting_table, player, craftinv, {recipe_counter_change = 1})
			show_setting_creator(player, "crafting", {})
		elseif fields.back then
			if fields.setting then
				player:set_attribute("inventorybags_setting_default", fields.setting)
				save_crafting_formspec(fields, setting_table, player, craftinv)
			end
			open_bud(player)
		end
		if fields.quit then
			minetest.after(0.1, open_bud, player)
		end
	end
end)

minetest.register_on_joinplayer(function(player)
	local inv = player:get_inventory()
	local playername = player:get_player_name()
	local spininvname = "inventorybags_"..playername.."_spinning_wheel"
	local loominvname = "inventorybags_"..playername.."_loom"
	local budinvname = "inventorybags_"..playername.."_bud"
	drop_inv(inv, player, spininvname.."_in")
	drop_inv(inv, player, spininvname.."_out")
	drop_inv(inv, player, loominvname.."_in")
	drop_inv(inv, player, loominvname.."_out")
	drop_inv(inv, player, budinvname.."_bag")
	drop_inv(inv, player, budinvname.."_upgrade")
	drop_inv(inv, player, budinvname.."_out")
end)


code:
inventorybags = {}
inventorybags.bud_recipes = {}
inventorybags.upgrade_help_string = "#ffff00General Info:," ..
	"Sometimes you have to have opened the bag the first time before the upgrade works.,"..
	"Each bag can have only one upgrade of every kind.,"..
	"You must put the upgrade into the upgrade slot before you can use the setting creator.," ..
	"Most upgrades don't work on special bags.," ..
	"You can upgrade and extract only one bag at once.," ..
	",#ffff00Bag Upgrades:,"
inventorybags.named_colors = {}

local colors = {}
colors["aliceblue"]       		 = "#f0f8ff"
colors["antiquewhite"]           = "#faebd7"
colors["aqua"]                   = "#00ffff"
colors["aquamarine"]             = "#7fffd4"
colors["azure"]                  = "#f0ffff"
colors["beige"]                  = "#f5f5dc"
colors["bisque"]                 = "#ffe4c4"
colors["black"]                  = "#000000"
colors["blanchedalmond"]         = "#ffebcd"
colors["blue"]                   = "#0000ff"
colors["blueviolet"]             = "#8a2be2"
colors["brown"]                  = "#a52a2a"
colors["burlywood"]              = "#deb887"
colors["cadetblue"]              = "#5f9ea0"
colors["chartreuse"]             = "#7fff00"
colors["chocolate"]              = "#d2691e"
colors["coral"]                  = "#ff7f50"
colors["cornflowerblue"]         = "#6495ed"
colors["cornsilk"]               = "#fff8dc"
colors["crimson"]                = "#dc143c"
colors["cyan"]                   = "#00ffff"
colors["darkblue"]               = "#00008b"
colors["darkcyan"]               = "#008b8b"
colors["darkgoldenrod"]          = "#b8860b"
colors["darkgray"]               = "#a9a9a9"
colors["darkgreen"]              = "#006400"
colors["darkgrey"]               = "#a9a9a9"
colors["darkkhaki"]              = "#bdb76b"
colors["darkmagenta"]            = "#8b008b"
colors["darkolivegreen"]         = "#556b2f"
colors["darkorange"]             = "#ff8c00"
colors["darkorchid"]             = "#9932cc"
colors["darkred"]                = "#8b0000"
colors["darksalmon"]             = "#e9967a"
colors["darkseagreen"]           = "#8fbc8f"
colors["darkslateblue"]          = "#483d8b"
colors["darkslategray"]          = "#2f4f4f"
colors["darkslategrey"]          = "#2f4f4f"
colors["darkturquoise"]          = "#00ced1"
colors["darkviolet"]             = "#9400d3"
colors["deeppink"]               = "#ff1493"
colors["deepskyblue"]            = "#00bfff"
colors["dimgray"]                = "#696969"
colors["dimgrey"]                = "#696969"
colors["dodgerblue"]             = "#1e90ff"
colors["firebrick"]              = "#b22222"
colors["floralwhite"]            = "#fffaf0"
colors["forestgreen"]            = "#228b22"
colors["fuchsia"]                = "#ff00ff"
colors["gainsboro"]              = "#dcdcdc"
colors["ghostwhite"]             = "#f8f8ff"
colors["gold"]                   = "#ffd700"
colors["goldenrod"]              = "#daa520"
colors["gray"]                   = "#808080"
colors["green"]                  = "#008000"
colors["greenyellow"]            = "#adff2f"
colors["grey"]                   = "#808080"
colors["honeydew"]               = "#f0fff0"
colors["hotpink"]                = "#ff69b4"
colors["indianred"]              = "#cd5c5c"
colors["indigo"]                 = "#4b0082"
colors["ivory"]                  = "#fffff0"
colors["khaki"]                  = "#f0e68c"
colors["lavender"]               = "#e6e6fa"
colors["lavenderblush"]          = "#fff0f5"
colors["lawngreen"]              = "#7cfc00"
colors["lemonchiffon"]           = "#fffacd"
colors["lightblue"]              = "#add8e6"
colors["lightcoral"]             = "#f08080"
colors["lightcyan"]              = "#e0ffff"
colors["lightgoldenrodyellow"]   = "#fafad2"
colors["lightgray"]              = "#d3d3d3"
colors["lightgreen"]             = "#90ee90"
colors["lightgrey"]              = "#d3d3d3"
colors["lightpink"]              = "#ffb6c1"
colors["lightsalmon"]            = "#ffa07a"
colors["lightseagreen"]          = "#20b2aa"
colors["lightskyblue"]           = "#87cefa"
colors["lightslategray"]         = "#778899"
colors["lightslategrey"]         = "#778899"
colors["lightsteelblue"]         = "#b0c4de"
colors["lightyellow"]            = "#ffffe0"
colors["lime"]                   = "#00ff00"
colors["limegreen"]              = "#32cd32"
colors["linen"]                  = "#faf0e6"
colors["magenta"]                = "#ff00ff"
colors["maroon"]                 = "#800000"
colors["mediumaquamarine"]       = "#66cdaa"
colors["mediumblue"]             = "#0000cd"
colors["mediumorchid"]           = "#ba55d3"
colors["mediumpurple"]           = "#9370db"
colors["mediumseagreen"]         = "#3cb371"
colors["mediumslateblue"]        = "#7b68ee"
colors["mediumspringgreen"]      = "#00fa9a"
colors["mediumturquoise"]        = "#48d1cc"
colors["mediumvioletred"]        = "#c71585"
colors["midnightblue"]           = "#191970"
colors["mintcream"]              = "#f5fffa"
colors["mistyrose"]              = "#ffe4e1"
colors["moccasin"]               = "#ffe4b5"
colors["navajowhite"]            = "#ffdead"
colors["navy"]                   = "#000080"
colors["oldlace"]                = "#fdf5e6"
colors["olive"]                  = "#808000"
colors["olivedrab"]              = "#6b8e23"
colors["orange"]                 = "#ffa500"
colors["orangered"]              = "#ff4500"
colors["orchid"]                 = "#da70d6"
colors["palegoldenrod"]          = "#eee8aa"
colors["palegreen"]              = "#98fb98"
colors["paleturquoise"]          = "#afeeee"
colors["palevioletred"]          = "#db7093"
colors["papayawhip"]             = "#ffefd5"
colors["peachpuff"]              = "#ffdab9"
colors["peru"]                   = "#cd853f"
colors["pink"]                   = "#ffc0cb"
colors["plum"]                   = "#dda0dd"
colors["powderblue"]             = "#b0e0e6"
colors["purple"]                 = "#800080"
colors["red"]                    = "#ff0000"
colors["rosybrown"]              = "#bc8f8f"
colors["royalblue"]              = "#4169e1"
colors["saddlebrown"]            = "#8b4513"
colors["salmon"]                 = "#fa8072"
colors["sandybrown"]             = "#f4a460"
colors["seagreen"]               = "#2e8b57"
colors["seashell"]               = "#fff5ee"
colors["sienna"]                 = "#a0522d"
colors["silver"]                 = "#c0c0c0"
colors["skyblue"]                = "#87ceeb"
colors["slateblue"]              = "#6a5acd"
colors["slategray"]              = "#708090"
colors["slategrey"]              = "#708090"
colors["snow"]                   = "#fffafa"
colors["springgreen"]            = "#00ff7f"
colors["steelblue"]              = "#4682b4"
colors["tan"]                    = "#d2b48c"
colors["teal"]                   = "#008080"
colors["thistle"]                = "#d8bfd8"
colors["tomato"]                 = "#ff6347"
colors["turquoise"]              = "#40e0d0"
colors["violet"]                 = "#ee82ee"
colors["wheat"]                  = "#f5deb3"
colors["white"]                  = "#ffffff"
colors["whitesmoke"]             = "#f5f5f5"
colors["yellow"]                 = "#ffff00"
colors["yellowgreen"]            = "#9acd32"

for colorname, color in pairs(colors) do
	table.insert(inventorybags.named_colors, {name = colorname, color = color})
end


code:
local modpath = minetest.get_modpath("inventorybags")
dofile(modpath .. "/globaltables.lua")
dofile(modpath .. "/craftitems.lua")
dofile(modpath .. "/bags.lua")
dofile(modpath .. "/devices.lua")
dofile(modpath .. "/sort_inventory_function.lua")
dofile(modpath .. "/upgrades.lua")
dofile(modpath .. "/crafting.lua")


code:
name = inventorybags
author = cx384
description = Adds bags
release = 305
title = Inventory Bags


code:
# Defaut off, because it doesn't work very well
inventorybags_enable_item_teleportation_bag(Enable Item Teleportation Bag) bool false

# Used to dialable the Bag of Winds
inventorybags_dialable_bag_of_winds(Dialables the Bag of Winds) bool false


code:
-- This function ( sort_inventory(inv) ) was taken form the technic_chests mod
-- https://github.com/minetest-mods/technic/blob/master/technic_chests/register.lua
-- License:
-- Copyright (C) 2012-2014 Maciej Kasatkin (RealBadAngel)
-- Technic chests code is licensed under the GNU LGPLv2+.

function inventorybags.sort_inventory(inv)
 	local inlist = inv:get_list("main")
 	local typecnt = {}
 	local typekeys = {}
 	for _, st in ipairs(inlist) do
 		if not st:is_empty() then
 			local n = st:get_name()
 			local w = st:get_wear()
 			local m = st:get_metadata()
 			local k = string.format("%s %05d %s", n, w, m)
 			if not typecnt[k] then
				typecnt[k] = {st}
 				table.insert(typekeys, k)
			else
				table.insert(typecnt[k], st)
 			end
 		end
 	end
 	table.sort(typekeys)
	inv:set_list("main", {})
 	for _, k in ipairs(typekeys) do
		for _, item in ipairs(typecnt[k]) do
			inv:add_item("main", item)
 		end
 	end
end


code:
local function register_upgrade(data)

	minetest.register_craftitem(data.name, {
		description = data.description,
		inventory_image = data.inventory_image,
		groups = {inventorybags_upgrade = 1}
	})
	if not data.meta_string then
		data.meta_string = "true"
	end
	local setting = ""
	if not data.help_setting then
		setting = "#7bff00Setting:,Irrelevant"
	else
		setting = "#7bff00Setting:," .. data.help_setting
	end

	inventorybags.bud_recipes[data.name] = {
		name = data.name,
		meta_name = data.meta_name,
		meta_string = data.meta_string,
		setting_meta_name = data.setting_meta_name,
		setting_type = data.setting_type
	}

	inventorybags.upgrade_help_string =
		inventorybags.upgrade_help_string .."," ..
		"#ffa500" .. data.description .. ": ,".. data.help .. "," ..
		setting .. ","
end

local function string_list_to_table(setstring)
	local new_table = {}
	local notstop = true
	while notstop do
		local numstring = string.find(setstring, ",")
		if numstring then
			table.insert(new_table, string.sub(setstring, 1, numstring-1))
			setstring = string.sub(setstring, numstring+1)
		else
			table.insert(new_table, setstring)
			notstop = false
		end
	end
	return new_table
end

local function get_omodname(stackname)
	return string.sub(stackname, 1, string.find(stackname, ":")-1)
end

local function get_oname(stackname)
	return string.sub(stackname, string.find(stackname, ":")+1)
end

local function table_contains(v, t)
	for _, i in pairs(t) do
		if i == v then
			return true
		end
	end
	return false
end

local function match_filter(filter_table, stackname)
	if stackname == "" or not stackname then
		return false
	end

	-- blacklist
	local blacklist_match = 0
	if type(filter_table.blacklist_items) == "table" and table.maxn(filter_table.blacklist_items) ~= 0 then
		if table_contains(stackname, filter_table.blacklist_items) then
			blacklist_match = blacklist_match + 1
		end
	end
	if type(filter_table.group_filter_blacklist) == "string" and filter_table.group_filter_blacklist ~= "" then
		for _, group in pairs(string_list_to_table(filter_table.group_filter_blacklist)) do
			if minetest.get_item_group(stackname, group) > 0 then
				blacklist_match = blacklist_match + 1
			end
		end
	end
	if type(filter_table.mod_filter_blacklist) == "string" and filter_table.mod_filter_blacklist ~= "" then
		if table_contains(get_omodname(stackname), string_list_to_table(filter_table.mod_filter_blacklist)) then
			blacklist_match = blacklist_match + 1
		end
	end
	if type(filter_table.name_filter_blacklist) == "string" and filter_table.name_filter_blacklist ~= "" then
		for _, text in pairs(string_list_to_table(filter_table.name_filter_blacklist)) do
			if string.find(get_oname(stackname),text) then
				blacklist_match = blacklist_match + 1
			end
		end
	end

	-- whitelist
	local whitelist_match = 0
	local whitelist_exist = false
	if type(filter_table.whitelist_items) == "table" and table.maxn(filter_table.whitelist_items) ~= 0 then
		whitelist_exist = true
		if table_contains(stackname, filter_table.whitelist_items) then
			whitelist_match = whitelist_match + 1
		end
	end
	if type(filter_table.group_filter_whitelist) == "string" and filter_table.group_filter_whitelist ~= "" then
		whitelist_exist = true
		for _, group in pairs(string_list_to_table(filter_table.group_filter_whitelist)) do
			if minetest.get_item_group(stackname, group) > 0 then
				whitelist_match = whitelist_match + 1
			end
		end
	end
	if type(filter_table.mod_filter_whitelist) == "string" and filter_table.mod_filter_whitelist ~= "" then
		whitelist_exist = true
		if table_contains(get_omodname(stackname), string_list_to_table(filter_table.mod_filter_whitelist)) then
			whitelist_match = whitelist_match + 1
		end
	end
	if type(filter_table.name_filter_whitelist) == "string" and filter_table.name_filter_whitelist ~= "" then
		whitelist_exist = true
		for _, text in pairs(string_list_to_table(filter_table.name_filter_whitelist)) do
			if string.find(get_oname(stackname),text) then
				whitelist_match = whitelist_match + 1
			end
		end
	end

	if blacklist_match > 0 or (whitelist_exist and whitelist_match == 0) then
		return false
	else
		return true
	end
end

-- Rename Upgrade

register_upgrade({
	name = "inventorybags:rename_upgrade",
	description = "Rename Upgrade",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_rename_upgrade.png",
	meta_name = "inventorybags_rename_upgrade",
	help = "Renames your bag.",
	setting_meta_name = "description",
	help_setting = 'The new name of your bag. ( Example: "The best bag in the world" )'
})

-- Coloring Upgrade

register_upgrade({
	name = "inventorybags:coloring_upgrade",
	description = "Coloring Upgrade",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_coloring_upgrade.png",
	meta_name = "inventorybags_coloring_upgrade",
	help = "Colors your bag.",
	setting_meta_name = "color",
	setting_type = "color",
	help_setting = 'The color of your bag. ( You can use the setting creator. )'
})

-- Storage Upgrades

register_upgrade({
	name = "inventorybags:storage_upgrade_tier_1",
	description = "Storage Upgrade Tier 1",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_storage_upgrade_tier_1.png",
	meta_name = "inventorybags_storage_upgrade_tier_1",
	help = "Increases the inventory height and width by at least one.",
})

register_upgrade({
	name = "inventorybags:storage_upgrade_tier_2",
	description = "Storage Upgrade Tier 2",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_storage_upgrade_tier_2.png",
	meta_name = "inventorybags_storage_upgrade_tier_2",
	help = "Increases the inventory height and width by at least one. ( You have to install all tiers. )",
})

register_upgrade({
	name = "inventorybags:storage_upgrade_tier_3",
	description = "Storage Upgrade Tier 3",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_storage_upgrade_tier_3.png",
	meta_name = "inventorybags_storage_upgrade_tier_3",
	help = "Increases the inventory height and width by at least one. ( You have to install all tiers. )",
})

local function increase_bag_size(width, height, bwidth, bheight)
	if bwidth > bheight then
		local wdh = bwidth / bheight
		height = height + 1
		width = bwidth + math.floor((height - bheight) * wdh)
	else
		local hdw = bheight / bwidth
		width = width + 1
		height = bheight + math.floor((width - bwidth) * hdw)
	end
	return width, height
end

local old_before_open_bag = inventorybags.before_open_bag
function inventorybags.before_open_bag(itemstack, user, width, height, sound)
	local meta = itemstack:get_meta()
	local bwidth, bheight = width, height
	if meta:get_string("inventorybags_storage_upgrade_tier_1") == "true" then
		width, height = increase_bag_size(width, height, bwidth, bheight)
	end
	if meta:get_string("inventorybags_storage_upgrade_tier_2") == "true" then
		width, height = increase_bag_size(width, height, bwidth, bheight)
	end
	if meta:get_string("inventorybags_storage_upgrade_tier_3") == "true" then
		width, height = increase_bag_size(width, height, bwidth, bheight)
	end
	return old_before_open_bag(itemstack, user, width, height, sound)
end

-- Collecting Upgrades

register_upgrade({
	name = "inventorybags:collecting_upgrade",
	description = "Collecting Upgrade",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_collecting_upgrade.png",
	meta_name = "inventorybags_collecting_upgrade",
	help = "Puts node drops into your bag.",
	setting_meta_name = "inventorybags_collecting_filter",
	setting_type = "filter",
	help_setting = 'A filter string ( use the setting creator ).'
})

register_upgrade({
	name = "inventorybags:advanced_collecting_upgrade",
	description = "Advanced Collecting Upgrade",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_advanced_collecting_upgrade.png",
	meta_name = "inventorybags_advanced_collecting_upgrade",
	help = "Puts all items into your bag.",
	setting_meta_name = "inventorybags_advanced_collecting_filter",
	setting_type = "filter",
	help_setting = 'A filter string ( use the setting creator ).'
})

local old_on_open_bag = inventorybags.on_open_bag
function inventorybags.on_open_bag(bagstack, baginv, player)
	local meta = bagstack:get_meta()
	local inv = player:get_inventory()
	local size = inv:get_size("main")
	if meta:get_string("inventorybags_advanced_collecting_upgrade") == "true" then
		for k = 1, size, 1 do
			local astack = inv:get_stack("main", k)
			if astack and minetest.get_item_group(astack:get_name(), "bag") == 0 then
				local fadd_item = true
				local filter_table = minetest.deserialize(meta:get_string("inventorybags_advanced_collecting_filter"))
				if filter_table then
					if not match_filter(filter_table, astack:get_name()) then
						fadd_item = false
					end
				end
				if fadd_item == true then
					if inv:room_for_item("main", astack) then
						inv:remove_item("main", astack)
						baginv:add_item("main", astack)
					end
				end
			end
		end
	end
	return old_on_open_bag(bagstack, baginv, player)
end

minetest.register_on_dignode(function(pos, oldnode, digger)
	if not digger then
		return
	end
	local inv = digger:get_inventory()
	if not inv then
		return
	end
	local drops = minetest.get_node_drops(oldnode.name, digger:get_wielded_item():get_name())
	local size = inv:get_size("main")
	local done_coll = false
	local done_acoll = false
	for i = 1, size, 1 do
		local stack = inv:get_stack("main", i)
		local meta = stack:get_meta()
		if minetest.get_item_group(stack:get_name(), "bag") > 0 then
			if meta:get_string("inventorybags_collecting_upgrade") == "true" and not done_coll then
				for drn, drop in pairs(drops) do
					if inv:contains_item("main", drop) and minetest.get_item_group(drop, "bag") == 0 then
						local fadd_item = true
						local filter_table = minetest.deserialize(meta:get_string("inventorybags_collecting_filter"))
						if filter_table then
							if not match_filter(filter_table, drop) then
								fadd_item = false
							end
						end
						if fadd_item == true then
							local newstack = inventorybags.bag_inv_add_item(stack, drop)
							if newstack then
								inv:remove_item("main", drop)
								inv:set_stack("main", i, newstack)
								done_coll = true
							end
						end
					end
				end
			end
			if meta:get_string("inventorybags_advanced_collecting_upgrade") == "true" and not done_acoll then
				for k = 1, size, 1 do
					local astack = inv:get_stack("main", k)
					if astack and minetest.get_item_group(astack:get_name(), "bag") == 0 then
						local fadd_item = true
						local filter_table = minetest.deserialize(meta:get_string("inventorybags_advanced_collecting_filter"))
						if filter_table then
							if not match_filter(filter_table, astack:get_name()) then
								fadd_item = false
							end
						end
						if fadd_item == true then
							local newstack = inventorybags.bag_inv_add_item(stack, astack)
							if newstack then
								inv:remove_item("main", astack)
								inv:set_stack("main", i, newstack)
								done_acoll = true
							end
						end
					end
				end
			end
		end
	end
end)

-- Sorting Upgrade

register_upgrade({
	name = "inventorybags:sorting_upgrade",
	description = "Sorting Upgrade",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_sorting_upgrade.png",
	meta_name = "inventorybags_sorting_upgrade",
	help = "Sorts all items in your bag."
})

local old_on_open_bag = inventorybags.on_open_bag
function inventorybags.on_open_bag(bagstack, baginv, player)
	local meta = bagstack:get_meta()
	if meta:get_string("inventorybags_sorting_upgrade") == "true" then
		inventorybags.sort_inventory(baginv)
	end
	return old_on_open_bag(bagstack, baginv, player)
end

-- Dumping Upgrade

register_upgrade({
	name = "inventorybags:dumping_upgrade",
	description = "Dumping Upgrade",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_dumping_upgrade.png",
	meta_name = "inventorybags_dumping_upgrade",
	help = "Destroys every item in your bag.",
	setting_meta_name = "inventorybags_dumping_filter",
	setting_type = "filter",
	help_setting = 'A filter string ( use the setting creator ).'
})

local old_on_change_bag_inv = inventorybags.on_change_bag_inv
function inventorybags.on_change_bag_inv(bagstack, baginv)
	local meta = bagstack:get_meta()
	if meta:get_string("inventorybags_dumping_upgrade") == "true" then
		local size = baginv:get_size("main")
		local filter_table = minetest.deserialize(meta:get_string("inventorybags_dumping_filter"))
		if filter_table then
			for i = 1, size, 1 do
				local stack = baginv:get_stack("main", i)
				if match_filter(filter_table, stack:get_name()) then
					baginv:set_stack("main", i, "")
				end
			end
		else
			for i = 1, size, 1 do
				baginv:set_stack("main", i, "")
			end
		end
	end
	return old_on_change_bag_inv(bagstack, baginv)
end

-- Sound Upgrades

register_upgrade({
	name = "inventorybags:opening_sound_upgrade",
	description = "Opening Sound Upgrade",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_sound_upgrade.png",
	meta_name = "inventorybags_opening_sound_upgrade",
	help = "Plays a sound when you are opening your bag.",
	setting_meta_name = "inventorybags_opening_sound",
	help_setting = 'The name of a sound. ( Examples: "inventorybags_open_zipper" "default_break_glass" "tnt_explode" )'
})

local old_before_open_bag = inventorybags.before_open_bag
function inventorybags.before_open_bag(itemstack, user, width, height, sound)
	local meta = itemstack:get_meta()
	if meta:get_string("inventorybags_opening_sound_upgrade") == "true" then
		sound = meta:get_string("inventorybags_opening_sound")
	end
	return old_before_open_bag(itemstack, user, width, height, sound)
end

register_upgrade({
	name = "inventorybags:closing_sound_upgrade",
	description = "Closing Sound Upgrade",
	inventory_image = "inventorybags_upgrade_base.png^(inventorybags_sound_upgrade.png^[transform2)",
	meta_name = "inventorybags_closing_sound_upgrade",
	help = "Plays a sound when you are closing your bag.",
	setting_meta_name = "inventorybags_closing_sound",
	help_setting = 'The name of a sound. ( Examples: "inventorybags_close_zipper" "default_dig_metal" "player_damage" )'
})

local old_on_close_bag = inventorybags.on_close_bag
function inventorybags.on_close_bag(player, fields, name, formname, sound)
	local inv = player:get_inventory()
	local size = inv:get_size("main")
	local bagstacki = nil
	for i = 1, size, 1 do
		local stack = inv:get_stack("main", i)
		local meta = stack:get_meta()
		if stack:get_name() == name and meta:get_string("inventorybags_bag_identity") == formname then
			bagstacki = i
			break
		end
	end
	if bagstacki then
		local itemstack = inv:get_stack("main", bagstacki)
		local meta = itemstack:get_meta()
		if meta:get_string("inventorybags_closing_sound_upgrade") == "true" then
			sound = meta:get_string("inventorybags_closing_sound")
		end
	end
	return old_on_close_bag(player, fields, name, formname, sound)
end

-- Autocrafting Upgrade

register_upgrade({
	name = "inventorybags:autocrafting_upgrade",
	description = "Autocrafting Upgrade",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_autocrafting_upgrade.png",
	meta_name = "inventorybags_autocrafting_upgrade",
	help = "Crafts items automatically.",
	setting_meta_name = "inventorybags_autocrafting_recipes",
	setting_type = "crafting",
	help_setting = 'A list of crafting recipes ( use the setting creator ).'
})

local function make_max_crafting(inv, recipes)
	local size = inv:get_size("main")
	local inv_table = {}
	for i = 1, size, 1 do
		local stack = inv:get_stack("main", i)
		local stack_name = stack:get_name()
		local stack_count = stack:get_count()
		if stack_count > 0 then
			if inv_table[stack_name] then
				inv_table[stack_name] = inv_table[stack_name] + stack_count
			else
				inv_table[stack_name] = stack_count
			end
		end
	end
	local recipes_table = {}
	for j, recipe in pairs(recipes) do
		local craft_result = minetest.get_craft_result({items = recipe, method = "normal", width = 3}).item
		if craft_result:get_count() > 0 then
			recipes_table[j] = {}
			for k, citem in pairs(recipe) do
				if citem ~= "" then
					if recipes_table[j][citem] then
						recipes_table[j][citem] = recipes_table[j][citem] + 1
					else
						recipes_table[j][citem] = 1
					end
				end
			end
		end
	end
	local rcounter = 1
	local nrecipes = #recipes_table
	while nrecipes > 0 do
		if rcounter > nrecipes then
			rcounter = 1
		end
		local recipe = recipes_table[nrecipes]
		local craft_results = minetest.get_craft_result({items = recipes[nrecipes], method = "normal", width = 3})
		local stop_sequence = false
		if not inv:room_for_item("main", craft_results.item) then
			stop_sequence = true
		elseif craft_results.replacements then
			for _, replacement in pairs(craft_results.replacements) do
				if not inv:room_for_item("main", replacement) then
					stop_sequence = true
				end
			end
		end
		if not stop_sequence then
			for citem, count in pairs(recipe) do
				if not( inv_table[citem] and inv_table[citem] >= count ) then
					stop_sequence = true
				end
			end
		end
		if not stop_sequence then
			for citem, count in pairs(recipe) do
				inv_table[citem] = inv_table[citem] - count
				inv:remove_item("main", {name = citem, count = count})
			end
			inv:add_item("main", craft_results.item)
			for _, replacement in pairs(craft_results.replacements) do
				inv:add_item("main", replacement)
			end
		else
			recipes_table[nrecipes] = nil
			nrecipes = nrecipes -1
		end
		rcounter = rcounter+1
	end
end

local old_on_change_bag_inv = inventorybags.on_change_bag_inv
function inventorybags.on_change_bag_inv(bagstack, baginv)
	local meta = bagstack:get_meta()
	if meta:get_string("inventorybags_autocrafting_upgrade") == "true" then
		local setting_table = minetest.deserialize(meta:get_string("inventorybags_autocrafting_recipes"))
		if setting_table then
			local recipes = setting_table.recipe_items
			if recipes then
				make_max_crafting(baginv, recipes)
			end
		end
	end
	return old_on_change_bag_inv(bagstack, baginv)
end

-- Refilling Upgrade

register_upgrade({
	name = "inventorybags:refilling_upgrade",
	description = "Refilling Upgrade",
	inventory_image = "inventorybags_upgrade_base.png^inventorybags_refilling_upgrade.png",
	meta_name = "inventorybags_refilling_upgrade",
	help = "Automatically refills your stack on placing.",
})

minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
	if not placer then
		return
	end
	local inv = placer:get_inventory()
	if not inv then
		return
	end
	local size = inv:get_size("main")
	for i = 1, size, 1 do
		local bagstack = inv:get_stack("main", i)
		local meta = bagstack:get_meta()
		if minetest.get_item_group(bagstack:get_name(), "bag") > 0 and
				meta:get_string("inventorybags_refilling_upgrade") == "true"  then
			local free_space = itemstack:get_free_space()
			local rem_bagstack = nil
			if free_space > 0 then
				rem_bagstack = inventorybags.bag_inv_remove_item(bagstack, itemstack:peek_item(1))
				if rem_bagstack then
					itemstack:set_count(itemstack:get_count()+1)
					free_space = itemstack:get_free_space()
				end
			end
			while free_space > 0 and rem_bagstack do
				bagstack = rem_bagstack
				itemstack:set_count(itemstack:get_count()+1)
				free_space = itemstack:get_free_space()
				rem_bagstack = inventorybags.bag_inv_remove_item(bagstack, itemstack:peek_item(1))
			end
			inv:set_stack("main", i, bagstack)
		end
	end
end)

-- Explosion Upgrade

local enable_tnt = minetest.settings:get_bool("enable_tnt")
if enable_tnt == nil then
	enable_tnt = minetest.is_singleplayer()
end

if minetest.get_modpath("tnt") and enable_tnt then

	register_upgrade({
		name = "inventorybags:explosion_upgrade",
		description = "Explosion Upgrade",
		inventory_image = "inventorybags_upgrade_base.png^inventorybags_explosion_upgrade.png",
		meta_name = "inventorybags_explosion_upgrade",
		help = "If you drop your bag it explodes after some time. ( You will lose the bag. ),"..
			"You can also hold sneak and left click to blast the bag instantly.,"..
			"The explosion radius depends on how much TNT do you have in your bag.",
		setting_meta_name = "inventorybags_explosion_upgrade_time",
		help_setting = 'The time before the bag explodes in seconds (1-30). (default 4)'
	})

	local old_on_drop_bag = inventorybags.on_drop_bag
	function inventorybags.on_drop_bag(itemstack, dropper, pos)
		local meta = itemstack:get_meta()
		if meta:get_string("inventorybags_explosion_upgrade") == "true" then
			local dtime = tonumber(meta:get_string("inventorybags_explosion_upgrade_time")) or 4
			if dtime > 30 then
				dtime = 30
			end
			local itemname = itemstack:get_name()
			local color = meta:get_string("color") or ""
			if color ~= "" and string.len(color) == 7 and string.sub(color,1,1) == "#" then
				color = "^[multiply:"..color
			else
				color = ""
			end
			local def = {}
			local tnt_radius = tonumber(minetest.settings:get("tnt_radius") or 3)
			local n = 0  --n >= 1
			local old_itemstack = itemstack
			while old_itemstack do
				n = n + 1
				old_itemstack = inventorybags.bag_inv_remove_item(itemstack, "tnt:tnt")
			end
			itemstack:take_item()
			def.radius = tnt_radius*(n^(1/3))
			def.damage_radius = def.radius * 2
			minetest.sound_play("tnt_ignite", {pos = pos})
			minetest.after(dtime, tnt.boom, pos, def)
			minetest.add_particle({
				pos = pos,
				expirationtime = dtime,
				size = def.radius * 2,
				collisiondetection = false,
				vertical = false,
				texture = minetest.registered_items[itemname].inventory_image..color,
			})
			return itemstack, dropper, pos
		end
		return old_on_drop_bag(itemstack, dropper, pos)
	end

	local old_on_use_bag = inventorybags.on_use_bag
	function inventorybags.on_use_bag(itemstack, user, pointed_thing)
		local meta = itemstack:get_meta()
		if meta:get_string("inventorybags_explosion_upgrade") == "true" and user:get_player_control().sneak == true then
			local pos = user:get_pos()
			local def = {}
			local tnt_radius = tonumber(minetest.settings:get("tnt_radius") or 3)
			local n = 0  --n >= 1
			local old_itemstack = itemstack
			while old_itemstack do
				n = n + 1
				old_itemstack = inventorybags.bag_inv_remove_item(itemstack, "tnt:tnt")
			end
			itemstack:take_item()
			def.radius = tnt_radius*(n^(1/3))
			def.damage_radius = def.radius * 2
			tnt.boom(pos, def)
			return itemstack, dropper, pos
		end
		return old_on_use_bag(itemstack, user, pointed_thing)
	end
end


code:
# LagCheck
[Minetest](https://www.minetest.net/ "Link to minetest.net") mod adding system to log the consumption of every mod

## Manual
#### For players
Use chatcommand `/lagcheck` to save the log file to the world folder. You need the privilege: privs

## Version
1.0

## License
CC BY-NC 3.0 | See [LICENSE](https://github.com/TalkLounge/lagcheck/blob/master/LICENSE.md "Link to LICENSE.md")

## Credits
**TalkLounge**
E-Mail: talklounge@yahoo.de
GitHub: [TalkLounge](https://github.com/TalkLounge/ "Link to TalkLounge's GitHub account")
Minetest: [TalkLounge](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=20862 "Link to TalkLounge's Minetest Forum account")

**Other contributors**
See: [Other contributors](https://github.com/TalkLounge/lagcheck/graphs/contributors "Link to other contributors")


code: Minetest mod adding system to log the consumption of every mod