Add client-side support for multiple ordered texture packs + Separate tabs for content (#17552)

This commit is contained in:
ZenonSeth
2026-10-10 13:46:29 +02:00
committed by GitHub
parent 9a1b92d0d4
commit 814ebcfc12
9 changed files with 174 additions and 34 deletions
+59 -5
View File
@@ -61,13 +61,17 @@ end
local function load_texture_packs(txtpath, retval)
local list = core.get_dir_list(txtpath, true)
local current_texture_path = core.settings:get("texture_path")
local enabled_packs = pkgmgr.get_enabled_texture_packs()
for _, item in ipairs(list) do
if item ~= "base" then
local path = txtpath .. DIR_DELIM .. item .. DIR_DELIM
local conf = Settings(path .. "texture_pack.conf")
local enabled = path == current_texture_path
local order = table.indexof(enabled_packs, path)
if order == -1 then
order = nil
end
local enabled = order ~= nil
local title = conf:get("title") or item
@@ -75,13 +79,14 @@ local function load_texture_packs(txtpath, retval)
retval[#retval + 1] = {
name = conf:get("name") or item,
title = title,
list_name = enabled and fgettext("$1 (Enabled)", item) or nil,
list_title = enabled and fgettext("$1 (Enabled)", title) or nil,
list_name = enabled and ("(" .. order .. ") " .. item) or nil,
list_title = enabled and ("(" .. order .. ") " .. title) or nil,
author = conf:get("author"),
release = tonumber(conf:get("release")) or 0,
type = "txp",
path = path,
enabled = enabled,
order = order,
}
end
end
@@ -116,6 +121,48 @@ function pkgmgr.get_mods(path, virtual_path, listing)
pkgmgr.update_translations(mods)
end
-- Must match PATH_DELIM in filesys.h
local PATH_DELIM = PLATFORM == "Windows" and ";" or ":"
--- Returns the ordered list of enabled texture pack paths, highest priority first.
function pkgmgr.get_enabled_texture_packs()
return (core.settings:get("texture_path") or ""):split(PATH_DELIM)
end
function pkgmgr.set_enabled_texture_packs(list)
core.settings:set("texture_path", table.concat(list, PATH_DELIM))
end
--- Newly enabled packs are appended at the end, lowest priority.
function pkgmgr.set_texture_pack_enabled(path, enabled)
local list = pkgmgr.get_enabled_texture_packs()
local index = table.indexof(list, path)
if index ~= -1 then
table.remove(list, index)
end
if enabled then
list[#list + 1] = path
end
pkgmgr.set_enabled_texture_packs(list)
end
--- Moves an enabled texture pack by `delta` places in the priority list.
function pkgmgr.move_texture_pack(path, delta)
local list = pkgmgr.get_enabled_texture_packs()
local index = table.indexof(list, path)
if index == -1 then
return
end
local new_index = math.max(1, math.min(#list, index + delta))
table.insert(list, new_index, table.remove(list, index))
pkgmgr.set_enabled_texture_packs(list)
end
--------------------------------------------------------------------------------
function pkgmgr.reload_texture_packs()
local txtpath = core.get_texturepath()
@@ -307,7 +354,14 @@ function pkgmgr.render_packagelist(render_list, use_technical_names, with_icon)
end
end
end
elseif v.enabled or v.type == "txp" then
elseif v.type == "txp" then
if v.enabled then
icon = 1
color = mt_color_green
else
color = mt_color_grey
end
elseif v.enabled then
icon = 1
color = mt_color_green
end
+2 -1
View File
@@ -11,7 +11,8 @@ local COLORS = {
--------------------------------------------------------------------------------
function mm_game_theme.init()
mm_game_theme.texturepack = core.settings:get("texture_path")
-- TODO: scan texture packs in order to get game theme images
mm_game_theme.texturepack = pkgmgr.get_enabled_texture_packs()[1]
mm_game_theme.gameid = nil
+89 -21
View File
@@ -13,6 +13,8 @@ local function get_content_icons(packages_with_updates)
end
local SUBTAB_KEYS = { "games", "mods", "txps" }
local packages_raw, packages
local function update_packages()
@@ -32,8 +34,25 @@ local function update_packages()
element.name == uid
end
packages = filterlist.create(get_data, pkgmgr.compare_package,
is_equal, nil, {})
local category_types = { game = "games", txp = "txps", mod = "mods", modpack = "mods" }
local function filter_by_category(element, category)
return category_types[element.type] == category
end
packages = filterlist.create(get_data, pkgmgr.compare_package, is_equal, filter_by_category, {})
end
-- Enabled packs first ordered by priority, then disabled packs alphabetically
local function sort_resources_list(list)
table.sort(list, function(a, b)
if a.enabled ~= b.enabled then
return a.enabled
end
if a.enabled then
return a.order < b.order
end
return a.title:lower() < b.title:lower()
end)
end
local function on_change(type)
@@ -48,8 +67,23 @@ local function get_formspec(tabview, name, tabdata)
update_packages()
end
if not tabdata.selected_pkg then
tabdata.selected_pkg = 1
tabdata.selected_pkg = tabdata.selected_pkg or 1
tabdata.subtab = tabdata.subtab or 1
local subtab_key = SUBTAB_KEYS[tabdata.subtab]
packages:set_filtercriteria(subtab_key)
if subtab_key == "txps" then
sort_resources_list(packages:get_list())
end
if tabdata.selected_path then
for i, pkg in ipairs(packages:get_list()) do
if pkg.path == tabdata.selected_path then
tabdata.selected_pkg = i
break
end
end
tabdata.selected_path = nil
end
local use_technical_names = core.settings:get_bool("show_technical_names")
@@ -66,19 +100,29 @@ local function get_formspec(tabview, name, tabdata)
end
local retval = {
"label[0.4,0.4;", fgettext("Installed Packages:"), "]",
string.format("tabheader[0.4,0.9;6.3,0.6;content_subtab;%s,%s,%s;%i;true;false]",
fgettext("Games"), fgettext("Mods"), fgettext("Texture packs"), tabdata.subtab),
"tablecolumns[color;tree;image,align=inline,width=1.5",
",tooltip=", fgettext("Update available?"),
",0=", core.formspec_escape(defaulttexturedir .. "blank.png"),
",4=", core.formspec_escape(defaulttexturedir .. "cdb_update_cropped.png"),
";text]",
"table[0.4,0.8;6.3,4.8;pkglist;",
"table[0.4,1.0;6.3,4.6;pkglist;",
pkgmgr.render_packagelist(packages, use_technical_names, update_icons),
";", tabdata.selected_pkg, "]",
"button[0.4,5.8;6.3,0.9;btn_contentdb;", contentdb_label, "]"
}
if subtab_key == "txps" then
local priority_tooltip = fgettext("Enabled texture packs are applied in priority order.") ..
"\n" .. fgettext("If two packs provide the same texture, the one listed first wins.")
table.insert_all(retval, {
"image[6.2,0.4;0.5,0.5;", core.formspec_escape(defaulttexturedir .. "settings_info.png"), "]",
"tooltip[6.2,0.4;0.5,0.5;", priority_tooltip, "]",
})
end
local selected_pkg
if filterlist.size(packages) >= tabdata.selected_pkg then
selected_pkg = packages:get_list()[tabdata.selected_pkg]
@@ -157,13 +201,27 @@ local function get_formspec(tabview, name, tabdata)
if selected_pkg.enabled then
table.insert_all(retval, {
"button[7.1,4.7;8,0.9;btn_mod_mgr_disable_txp;",
"button[7.1,4.7;4,0.9;btn_mod_mgr_disable_txp;",
fgettext("Disable Texture Pack"), "]"
})
local enabled_count = #pkgmgr.get_enabled_texture_packs()
if selected_pkg.order > 1 then
table.insert_all(retval, {
"button[11.1,4.7;2,0.9;btn_mod_mgr_txp_move_up;",
fgettext("Move Up"), "]"
})
end
if selected_pkg.order < enabled_count then
table.insert_all(retval, {
"button[13.1,4.7;2,0.9;btn_mod_mgr_txp_move_down;",
fgettext("Move Down"), "]"
})
end
else
table.insert_all(retval, {
"button[7.1,4.7;8,0.9;btn_mod_mgr_use_txp;",
fgettext("Use Texture Pack"), "]"
fgettext("Enable Texture Pack"), "]"
})
end
end
@@ -193,13 +251,10 @@ local function get_formspec(tabview, name, tabdata)
return table.concat(retval)
end
local function handle_doubleclick(pkg)
local function handle_doubleclick(pkg, tabdata)
if pkg.type == "txp" then
if core.settings:get("texture_path") == pkg.path then
core.settings:set("texture_path", "")
else
core.settings:set("texture_path", pkg.path)
end
pkgmgr.set_texture_pack_enabled(pkg.path, not pkg.enabled)
tabdata.selected_path = pkg.path
packages = nil
pkgmgr.reload_texture_packs()
@@ -210,11 +265,17 @@ end
local function handle_buttons(tabview, fields, tabname, tabdata)
if fields.content_subtab then
tabdata.subtab = tonumber(fields.content_subtab)
tabdata.selected_pkg = 1
return true
end
if fields.pkglist then
local event = core.explode_table_event(fields.pkglist)
tabdata.selected_pkg = event.row
if event.type == "DCL" then
handle_doubleclick(packages:get_list()[tabdata.selected_pkg])
handle_doubleclick(packages:get_list()[tabdata.selected_pkg], tabdata)
end
return true
end
@@ -259,12 +320,9 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
end
if fields.btn_mod_mgr_use_txp or fields.btn_mod_mgr_disable_txp then
local txp_path = ""
if fields.btn_mod_mgr_use_txp then
txp_path = packages:get_list()[tabdata.selected_pkg].path
end
core.settings:set("texture_path", txp_path)
local pkg = packages:get_list()[tabdata.selected_pkg]
pkgmgr.set_texture_pack_enabled(pkg.path, fields.btn_mod_mgr_use_txp ~= nil)
tabdata.selected_path = pkg.path
packages = nil
pkgmgr.reload_texture_packs()
@@ -273,6 +331,16 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
return true
end
if fields.btn_mod_mgr_txp_move_up or fields.btn_mod_mgr_txp_move_down then
local pkg = packages:get_list()[tabdata.selected_pkg]
local delta = fields.btn_mod_mgr_txp_move_up and -1 or 1
pkgmgr.move_texture_pack(pkg.path, delta)
tabdata.selected_path = pkg.path
packages = nil
pkgmgr.reload_texture_packs()
return true
end
return false
end
+5 -2
View File
@@ -2503,8 +2503,11 @@ serverlist_file (Serverlist file) [client] string favoriteservers.json
[*Hide: Temporary Settings] [common]
# Path to texture directory. All textures are first searched from here.
texture_path (Texture path) path
# Ordered list of texture pack paths, separated by ";" on Windows
# and ":" on other platforms.
# Earlier entries take priority: if multiple packs provide the same
# texture, the first one wins.
texture_path (Texture path) string
# Enables minimap.
enable_minimap (Minimap) bool true
+2 -2
View File
@@ -15,7 +15,6 @@
#include "client/particles.h"
#include "client/renderingengine.h"
#include "client/sound.h"
#include "client/texturepaths.h"
#include "client/texturesource.h"
#include "camera.h"
#include "filesys.h"
@@ -1915,7 +1914,8 @@ void Client::afterContentReceived()
m_rendering_engine->draw_load_screen(wstrgettext("Initializing nodes..."),
guienv, m_tsrc, 0, 70);
m_nodedef->updateAliases(m_itemdef);
for (const auto &path : getTextureDirs()) {
// Apply texture overrides from texturepack/override.txt
for (const std::string &path : getTexturePathsReversed()) {
TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
+2 -1
View File
@@ -99,7 +99,8 @@ std::vector<std::string> getTextureDirs()
{
std::vector<std::string> ret = g_texturedirs_cache.get();
if (ret.empty()) {
ret = fs::GetRecursiveDirs(g_settings->get("texture_path"));
for (const std::string &pack_path : str_split(g_settings->get("texture_path"), PATH_DELIM[0]))
fs::GetRecursiveDirs(ret, pack_path);
g_texturedirs_cache.set(ret);
}
return ret;
+1 -2
View File
@@ -538,8 +538,7 @@ void Server::init()
m_nodedef->updateAliases(m_itemdef);
// Apply texture overrides from texturepack/override.txt
std::vector<std::string> paths;
fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
std::vector<std::string> paths = getTexturePathsReversed();
fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
for (const std::string &path : paths) {
TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
+11
View File
@@ -6,6 +6,7 @@
#include "log.h"
#include "filesys.h"
#include "settings.h"
#include "util/string.h"
#include <map>
@@ -133,3 +134,13 @@ std::vector<TextureOverride> TextureOverrideSource::getNodeTileOverrides() const
return found_overrides;
}
std::vector<std::string> getTexturePathsReversed()
{
std::vector<std::string> paths;
// reversed: applyTextureOverrides() so highest priority gets applied last, aka overrides others
std::vector<std::string> pack_paths = str_split(g_settings->get("texture_path"), PATH_DELIM[0]);
for (auto it = pack_paths.rbegin(); it != pack_paths.rend(); ++it)
fs::GetRecursiveDirs(paths, *it);
return paths;
}
+3
View File
@@ -74,3 +74,6 @@ public:
private:
std::vector<TextureOverride> m_overrides;
};
//! Texture pack dirs from the texture_path setting, lowest priority pack first
std::vector<std::string> getTexturePathsReversed();