local modpath = core.get_modpath "i3" local function lf(path) return loadfile(modpath .. path) end i3 = { modules = {}, http = core.request_http_api(), MAX_FAVS = 6, INV_SIZE = 4*9, HOTBAR_LEN = 9, ITEM_BTN_SIZE = 1.1, MIN_FORMSPEC_VERSION = 4, SAVE_INTERVAL = 600, -- Player data save interval (in seconds) BAG_SIZES = { 4*9 + 3, 4*9 + 6, 4*9 + 9, }, SUBCAT = { "bag", "armor", "skins", "awards", "waypoints", }, META_SAVES = { bag_item = true, bag_size = true, waypoints = true, inv_items = true, known_recipes = true, }, -- Caches init_items = {}, recipes_cache = {}, usages_cache = {}, fuel_cache = {}, recipe_filters = {}, search_filters = {}, craft_types = {}, tabs = {}, files = { api = lf("/etc/api.lua"), bags = lf("/etc/bags.lua"), common = lf("/etc/common.lua"), compress = lf("/etc/compress.lua"), groups = lf("/etc/groups.lua"), gui = lf("/etc/gui.lua"), inventory = lf("/etc/inventory.lua"), model_alias = lf("/etc/model_aliases.lua"), progressive = lf("/etc/progressive.lua"), recipes = lf("/etc/recipes.lua"), styles = lf("/etc/styles.lua"), }, progressive_mode = core.settings:get_bool "i3_progressive_mode", item_compression = core.settings:get_bool("i3_item_compression", true), } local common = i3.files.common() function i3.need(...) local t = {} for _, var in ipairs {...} do for _, cat in pairs(common) do for name, func in pairs(cat) do if var == name then t[#t + 1] = func break end end end end return unpack(t) end local storage = core.get_mod_storage() local slz, dslz = core.serialize, core.deserialize i3.data = dslz(storage:get_string "data") or {} i3.compress_groups, i3.compressed = i3.files.compress() i3.group_stereotypes, i3.group_names = i3.files.groups() local set_fs = i3.files.api() local init_backpack = i3.files.bags() i3.files.inventory() local cache_drops, cache_fuel, cache_recipes, cache_usages, resolve_aliases = i3.files.recipes() local fmt, sort, copy = i3.need("fmt", "sort", "copy") local show_item, reg_items = i3.need("show_item", "reg_items") local function get_lang_code(info) return info and info.lang_code end local function get_formspec_version(info) return info and info.formspec_version or 1 end local function outdated(name) local fs = fmt("size[6.3,1.3]image[0,0;1,1;i3_book.png]label[1,0;%s]button_exit[2.6,0.8;1,1;;OK]", "Your Minetest client is outdated.\nGet the latest version on minetest.net to play the game.") core.show_formspec(name, "i3_outdated", fs) end local function init_data(player, info) local name = player:get_player_name() i3.data[name] = i3.data[name] or {} local data = i3.data[name] data.filter = "" data.pagenum = 1 data.items = i3.init_items data.items_raw = i3.init_items data.favs = {} data.export_counts = {} data.current_tab = 1 data.current_itab = 1 data.subcat = 1 data.scrbar_inv = 0 data.lang_code = get_lang_code(info) data.fs_version = info.formspec_version core.after(0, set_fs, player) end if rawget(_G, "armor") then i3.modules.armor = true armor:register_on_update(set_fs) end if rawget(_G, "skins") then i3.modules.skins = true end if rawget(_G, "awards") then i3.modules.awards = true core.register_on_craft(function(_, player) set_fs(player) end) core.register_on_dignode(function(_, _, player) set_fs(player) end) core.register_on_placenode(function(_, _, player) set_fs(player) end) core.register_on_chat_message(function(name) local player = core.get_player_by_name(name) set_fs(player) end) end local function get_init_items() local _select, _preselect = {}, {} for name, def in pairs(reg_items) do if name ~= "" and show_item(def) then cache_drops(name, def.drop) cache_fuel(name) cache_recipes(name) _preselect[name] = true end end for name in pairs(_preselect) do cache_usages(name) i3.init_items[#i3.init_items + 1] = name _select[name] = true end resolve_aliases(_select) sort(i3.init_items) if i3.http and type(i3.export_url) == "string" then local post_data = { recipes = i3.recipes_cache, usages = i3.usages_cache, } i3.http.fetch_async { url = i3.export_url, post_data = core.write_json(post_data), } end end core.register_on_mods_loaded(function() get_init_items() if rawget(_G, "sfinv") then function sfinv.set_player_inventory_formspec() return end sfinv.enabled = false end if rawget(_G, "unified_inventory") then function unified_inventory.set_inventory_formspec() return end end end) local function init_waypoints(player) local name = player:get_player_name() local data = i3.data[name] data.waypoints = data.waypoints or {} for _, v in ipairs(data.waypoints) do if not v.hide then local id = player:hud_add { hud_elem_type = "waypoint", name = v.name, text = " m", world_pos = v.pos, number = v.color, z_index = -300, } v.id = id end end end core.register_on_joinplayer(function(player) local name = player:get_player_name() local info = core.get_player_information and core.get_player_information(name) if not info or get_formspec_version(info) < i3.MIN_FORMSPEC_VERSION then i3.data[name] = nil return outdated(name) end init_data(player, info) init_backpack(player) init_waypoints(player) core.after(0, function() player:hud_set_hotbar_itemcount(i3.HOTBAR_LEN) player:hud_set_hotbar_image("i3_hotbar.png") end) end) local function save_data(player_name) local _data = copy(i3.data) for name, v in pairs(_data) do for dat in pairs(v) do if not i3.META_SAVES[dat] then _data[name][dat] = nil if player_name and i3.data[player_name] then i3.data[player_name][dat] = nil -- To free up some memory end end end end storage:set_string("data", slz(_data)) end core.register_on_leaveplayer(function(player) local name = player:get_player_name() save_data(name) end) core.register_on_shutdown(save_data) local function routine() save_data() core.after(i3.SAVE_INTERVAL, routine) end core.after(i3.SAVE_INTERVAL, routine) if i3.progressive_mode then i3.files.progressive() end --dofile(modpath .. "/tests/test_tabs.lua") --dofile(modpath .. "/tests/test_compression.lua") --dofile(modpath .. "/tests/test_custom_recipes.lua")