mirror of
https://github.com/minetest/minetest.git
synced 2024-11-26 09:33:45 +01:00
Add check_mod_configuration to main menu
This commit is contained in:
parent
06de82fd86
commit
9f41b4f72d
@ -61,12 +61,68 @@ local function init_data(data)
|
|||||||
data.list:set_sortmode("alphabetic")
|
data.list:set_sortmode("alphabetic")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Returns errors errors and a list of all enabled mods (inc. game and world mods)
|
||||||
|
--
|
||||||
|
-- `with_errors` is a table from mod virtual path to `{ type = "error" | "warning" }`.
|
||||||
|
-- `enabled_mods_by_name` is a table from mod virtual path to `true`.
|
||||||
|
--
|
||||||
|
-- @param world_path Path to the world
|
||||||
|
-- @param all_mods List of mods, with `enabled` property.
|
||||||
|
-- @returns with_errors, enabled_mods_by_name
|
||||||
|
local function check_mod_configuration(world_path, all_mods)
|
||||||
|
-- Build up lookup tables for enabled mods and all mods by vpath
|
||||||
|
local enabled_mod_paths = {}
|
||||||
|
local all_mods_by_vpath = {}
|
||||||
|
for _, mod in ipairs(all_mods) do
|
||||||
|
if mod.type == "mod" then
|
||||||
|
all_mods_by_vpath[mod.virtual_path] = mod
|
||||||
|
end
|
||||||
|
if mod.enabled then
|
||||||
|
enabled_mod_paths[mod.virtual_path] = mod.path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Use the engine's mod configuration code to resolve dependencies and return any errors
|
||||||
|
local config_status = core.check_mod_configuration(world_path, enabled_mod_paths)
|
||||||
|
|
||||||
|
-- Build the list of enabled mod virtual paths
|
||||||
|
local enabled_mods_by_name = {}
|
||||||
|
for _, mod in ipairs(config_status.satisfied_mods) do
|
||||||
|
assert(mod.virtual_path ~= "")
|
||||||
|
enabled_mods_by_name[mod.name] = all_mods_by_vpath[mod.virtual_path] or mod
|
||||||
|
end
|
||||||
|
for _, mod in ipairs(config_status.unsatisfied_mods) do
|
||||||
|
assert(mod.virtual_path ~= "")
|
||||||
|
enabled_mods_by_name[mod.name] = all_mods_by_vpath[mod.virtual_path] or mod
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Build the table of errors
|
||||||
|
local with_error = {}
|
||||||
|
for _, mod in ipairs(config_status.unsatisfied_mods) do
|
||||||
|
local error = { type = "warning" }
|
||||||
|
with_error[mod.virtual_path] = error
|
||||||
|
|
||||||
|
for _, depname in ipairs(mod.unsatisfied_depends) do
|
||||||
|
if not enabled_mods_by_name[depname] then
|
||||||
|
error.type = "error"
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return with_error, enabled_mods_by_name
|
||||||
|
end
|
||||||
|
|
||||||
local function get_formspec(data)
|
local function get_formspec(data)
|
||||||
if not data.list then
|
if not data.list then
|
||||||
init_data(data)
|
init_data(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
local mod = data.list:get_list()[data.selected_mod] or {name = ""}
|
local all_mods = data.list:get_list()
|
||||||
|
local with_error, enabled_mods_by_name = check_mod_configuration(data.worldspec.path, all_mods)
|
||||||
|
|
||||||
|
local mod = all_mods[data.selected_mod] or {name = ""}
|
||||||
|
|
||||||
local retval =
|
local retval =
|
||||||
"size[11.5,7.5,true]" ..
|
"size[11.5,7.5,true]" ..
|
||||||
@ -87,6 +143,29 @@ local function get_formspec(data)
|
|||||||
"textarea[0.25,0.7;5.75,7.2;;" .. info .. ";]"
|
"textarea[0.25,0.7;5.75,7.2;;" .. info .. ";]"
|
||||||
else
|
else
|
||||||
local hard_deps, soft_deps = pkgmgr.get_dependencies(mod.path)
|
local hard_deps, soft_deps = pkgmgr.get_dependencies(mod.path)
|
||||||
|
|
||||||
|
-- Add error messages to dep lists
|
||||||
|
if mod.enabled or mod.is_game_content then
|
||||||
|
for i, dep_name in ipairs(hard_deps) do
|
||||||
|
local dep = enabled_mods_by_name[dep_name]
|
||||||
|
if not dep then
|
||||||
|
hard_deps[i] = mt_color_red .. dep_name .. " " .. fgettext("(Unsatisfied)")
|
||||||
|
elseif with_error[dep.virtual_path] then
|
||||||
|
hard_deps[i] = mt_color_orange .. dep_name .. " " .. fgettext("(Enabled, has error)")
|
||||||
|
else
|
||||||
|
hard_deps[i] = mt_color_green .. dep_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i, dep_name in ipairs(soft_deps) do
|
||||||
|
local dep = enabled_mods_by_name[dep_name]
|
||||||
|
if dep and with_error[dep.virtual_path] then
|
||||||
|
soft_deps[i] = mt_color_orange .. dep_name .. " " .. fgettext("(Enabled, has error)")
|
||||||
|
elseif dep then
|
||||||
|
soft_deps[i] = mt_color_green .. dep_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local hard_deps_str = table.concat(hard_deps, ",")
|
local hard_deps_str = table.concat(hard_deps, ",")
|
||||||
local soft_deps_str = table.concat(soft_deps, ",")
|
local soft_deps_str = table.concat(soft_deps, ",")
|
||||||
|
|
||||||
@ -138,7 +217,6 @@ local function get_formspec(data)
|
|||||||
|
|
||||||
if mod.name ~= "" and not mod.is_game_content then
|
if mod.name ~= "" and not mod.is_game_content then
|
||||||
if mod.is_modpack then
|
if mod.is_modpack then
|
||||||
|
|
||||||
if pkgmgr.is_modpack_entirely_enabled(data, mod.name) then
|
if pkgmgr.is_modpack_entirely_enabled(data, mod.name) then
|
||||||
retval = retval ..
|
retval = retval ..
|
||||||
"button[5.5,0.125;3,0.5;btn_mp_disable;" ..
|
"button[5.5,0.125;3,0.5;btn_mp_disable;" ..
|
||||||
@ -167,9 +245,12 @@ local function get_formspec(data)
|
|||||||
local use_technical_names = core.settings:get_bool("show_technical_names")
|
local use_technical_names = core.settings:get_bool("show_technical_names")
|
||||||
|
|
||||||
return retval ..
|
return retval ..
|
||||||
"tablecolumns[color;tree;text]" ..
|
"tablecolumns[color;tree;image,align=inline,width=1.5,0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") ..
|
||||||
|
",1=" .. core.formspec_escape(defaulttexturedir .. "checkbox_16_white.png") ..
|
||||||
|
",2=" .. core.formspec_escape(defaulttexturedir .. "error_icon_orange.png") ..
|
||||||
|
",3=" .. core.formspec_escape(defaulttexturedir .. "error_icon_red.png") .. ";text]" ..
|
||||||
"table[5.5,0.75;5.75,6;world_config_modlist;" ..
|
"table[5.5,0.75;5.75,6;world_config_modlist;" ..
|
||||||
pkgmgr.render_packagelist(data.list, use_technical_names) .. ";" .. data.selected_mod .."]"
|
pkgmgr.render_packagelist(data.list, use_technical_names, with_error) .. ";" .. data.selected_mod .."]"
|
||||||
end
|
end
|
||||||
|
|
||||||
local function handle_buttons(this, fields)
|
local function handle_buttons(this, fields)
|
||||||
|
@ -21,6 +21,7 @@ mt_color_lightblue = "#99CCFF"
|
|||||||
mt_color_green = "#72FF63"
|
mt_color_green = "#72FF63"
|
||||||
mt_color_dark_green = "#25C191"
|
mt_color_dark_green = "#25C191"
|
||||||
mt_color_orange = "#FF8800"
|
mt_color_orange = "#FF8800"
|
||||||
|
mt_color_red = "#FF3300"
|
||||||
|
|
||||||
local menupath = core.get_mainmenu_path()
|
local menupath = core.get_mainmenu_path()
|
||||||
local basepath = core.get_builtin_path()
|
local basepath = core.get_builtin_path()
|
||||||
|
@ -337,7 +337,7 @@ function pkgmgr.identify_modname(modpath,filename)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function pkgmgr.render_packagelist(render_list, use_technical_names)
|
function pkgmgr.render_packagelist(render_list, use_technical_names, with_error)
|
||||||
if not render_list then
|
if not render_list then
|
||||||
if not pkgmgr.global_mods then
|
if not pkgmgr.global_mods then
|
||||||
pkgmgr.refresh_globals()
|
pkgmgr.refresh_globals()
|
||||||
@ -349,24 +349,59 @@ function pkgmgr.render_packagelist(render_list, use_technical_names)
|
|||||||
local retval = {}
|
local retval = {}
|
||||||
for i, v in ipairs(list) do
|
for i, v in ipairs(list) do
|
||||||
local color = ""
|
local color = ""
|
||||||
|
local icon = 0
|
||||||
|
local error = with_error and with_error[v.virtual_path]
|
||||||
|
local function update_error(val)
|
||||||
|
if val and (not error or (error.type == "warning" and val.type == "error")) then
|
||||||
|
error = val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if v.is_modpack then
|
if v.is_modpack then
|
||||||
local rawlist = render_list:get_raw_list()
|
local rawlist = render_list:get_raw_list()
|
||||||
color = mt_color_dark_green
|
color = mt_color_dark_green
|
||||||
|
|
||||||
for j = 1, #rawlist, 1 do
|
for j = 1, #rawlist do
|
||||||
if rawlist[j].modpack == list[i].name and
|
if rawlist[j].modpack == list[i].name then
|
||||||
not rawlist[j].enabled then
|
if with_error then
|
||||||
-- Modpack not entirely enabled so showing as grey
|
update_error(with_error[rawlist[j].virtual_path])
|
||||||
color = mt_color_grey
|
end
|
||||||
break
|
|
||||||
|
if rawlist[j].enabled then
|
||||||
|
icon = 1
|
||||||
|
else
|
||||||
|
-- Modpack not entirely enabled so showing as grey
|
||||||
|
color = mt_color_grey
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif v.is_game_content or v.type == "game" then
|
elseif v.is_game_content or v.type == "game" then
|
||||||
|
icon = 1
|
||||||
color = mt_color_blue
|
color = mt_color_blue
|
||||||
|
|
||||||
|
local rawlist = render_list:get_raw_list()
|
||||||
|
if v.type == "game" and with_error then
|
||||||
|
for j = 1, #rawlist do
|
||||||
|
if rawlist[j].is_game_content then
|
||||||
|
update_error(with_error[rawlist[j].virtual_path])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
elseif v.enabled or v.type == "txp" then
|
elseif v.enabled or v.type == "txp" then
|
||||||
|
icon = 1
|
||||||
color = mt_color_green
|
color = mt_color_green
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if error then
|
||||||
|
if error.type == "warning" then
|
||||||
|
color = mt_color_orange
|
||||||
|
icon = 2
|
||||||
|
else
|
||||||
|
color = mt_color_red
|
||||||
|
icon = 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
retval[#retval + 1] = color
|
retval[#retval + 1] = color
|
||||||
if v.modpack ~= nil or v.loc == "game" then
|
if v.modpack ~= nil or v.loc == "game" then
|
||||||
retval[#retval + 1] = "1"
|
retval[#retval + 1] = "1"
|
||||||
@ -374,6 +409,10 @@ function pkgmgr.render_packagelist(render_list, use_technical_names)
|
|||||||
retval[#retval + 1] = "0"
|
retval[#retval + 1] = "0"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if with_error then
|
||||||
|
retval[#retval + 1] = icon
|
||||||
|
end
|
||||||
|
|
||||||
if use_technical_names then
|
if use_technical_names then
|
||||||
retval[#retval + 1] = core.formspec_escape(v.list_name or v.name)
|
retval[#retval + 1] = core.formspec_escape(v.list_name or v.name)
|
||||||
else
|
else
|
||||||
@ -503,7 +542,7 @@ function pkgmgr.enable_mod(this, toset)
|
|||||||
if not mod_to_enable then
|
if not mod_to_enable then
|
||||||
core.log("warning", "Mod dependency \"" .. name ..
|
core.log("warning", "Mod dependency \"" .. name ..
|
||||||
"\" not found!")
|
"\" not found!")
|
||||||
else
|
elseif not mod_to_enable.is_game_content then
|
||||||
if not mod_to_enable.enabled then
|
if not mod_to_enable.enabled then
|
||||||
mod_to_enable.enabled = true
|
mod_to_enable.enabled = true
|
||||||
toggled_mods[#toggled_mods+1] = mod_to_enable.name
|
toggled_mods[#toggled_mods+1] = mod_to_enable.name
|
||||||
|
@ -276,7 +276,18 @@ Package - content which is downloadable from the content db, may or may not be i
|
|||||||
depends = {"mod", "names"}, -- mods only
|
depends = {"mod", "names"}, -- mods only
|
||||||
optional_depends = {"mod", "names"}, -- mods only
|
optional_depends = {"mod", "names"}, -- mods only
|
||||||
}
|
}
|
||||||
|
* core.check_mod_configuration(world_path, mod_paths)
|
||||||
|
* Checks whether configuration is valid.
|
||||||
|
* `world_path`: path to the world
|
||||||
|
* `mod_paths`: list of enabled mod paths
|
||||||
|
* returns:
|
||||||
|
|
||||||
|
{
|
||||||
|
is_consistent = true, -- true is consistent, false otherwise
|
||||||
|
unsatisfied_mods = {}, -- list of mod specs
|
||||||
|
satisfied_mods = {}, -- list of mod specs
|
||||||
|
error_message = "", -- message or nil
|
||||||
|
}
|
||||||
|
|
||||||
Logging
|
Logging
|
||||||
-------
|
-------
|
||||||
|
@ -2143,3 +2143,35 @@ void push_collision_move_result(lua_State *L, const collisionMoveResult &res)
|
|||||||
lua_setfield(L, -2, "collisions");
|
lua_setfield(L, -2, "collisions");
|
||||||
/**/
|
/**/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void push_mod_spec(lua_State *L, const ModSpec &spec, bool include_unsatisfied)
|
||||||
|
{
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
lua_pushstring(L, spec.name.c_str());
|
||||||
|
lua_setfield(L, -2, "name");
|
||||||
|
|
||||||
|
lua_pushstring(L, spec.author.c_str());
|
||||||
|
lua_setfield(L, -2, "author");
|
||||||
|
|
||||||
|
lua_pushinteger(L, spec.release);
|
||||||
|
lua_setfield(L, -2, "release");
|
||||||
|
|
||||||
|
lua_pushstring(L, spec.desc.c_str());
|
||||||
|
lua_setfield(L, -2, "description");
|
||||||
|
|
||||||
|
lua_pushstring(L, spec.path.c_str());
|
||||||
|
lua_setfield(L, -2, "path");
|
||||||
|
|
||||||
|
lua_pushstring(L, spec.virtual_path.c_str());
|
||||||
|
lua_setfield(L, -2, "virtual_path");
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
int i = 1;
|
||||||
|
for (const auto &dep : spec.unsatisfied_depends) {
|
||||||
|
lua_pushstring(L, dep.c_str());
|
||||||
|
lua_rawseti(L, -2, i++);
|
||||||
|
}
|
||||||
|
lua_setfield(L, -2, "unsatisfied_depends");
|
||||||
|
}
|
||||||
|
@ -42,6 +42,7 @@ extern "C" {
|
|||||||
// We do a explicit path include because by default c_content.h include src/client/hud.h
|
// We do a explicit path include because by default c_content.h include src/client/hud.h
|
||||||
// prior to the src/hud.h, which is not good on server only build
|
// prior to the src/hud.h, which is not good on server only build
|
||||||
#include "../../hud.h"
|
#include "../../hud.h"
|
||||||
|
#include "content/mods.h"
|
||||||
|
|
||||||
namespace Json { class Value; }
|
namespace Json { class Value; }
|
||||||
|
|
||||||
@ -204,3 +205,5 @@ void push_hud_element (lua_State *L, HudElement *elem);
|
|||||||
bool read_hud_change (lua_State *L, HudElementStat &stat, HudElement *elem, void **value);
|
bool read_hud_change (lua_State *L, HudElementStat &stat, HudElement *elem, void **value);
|
||||||
|
|
||||||
void push_collision_move_result(lua_State *L, const collisionMoveResult &res);
|
void push_collision_move_result(lua_State *L, const collisionMoveResult &res);
|
||||||
|
|
||||||
|
void push_mod_spec(lua_State *L, const ModSpec &spec, bool include_unsatisfied);
|
||||||
|
@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "client/client.h"
|
#include "client/client.h"
|
||||||
#include "client/renderingengine.h"
|
#include "client/renderingengine.h"
|
||||||
#include "network/networkprotocol.h"
|
#include "network/networkprotocol.h"
|
||||||
|
#include "content/mod_configuration.h"
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
std::string ModApiMainMenu::getTextData(lua_State *L, std::string name)
|
std::string ModApiMainMenu::getTextData(lua_State *L, std::string name)
|
||||||
@ -409,6 +409,100 @@ int ModApiMainMenu::l_get_content_info(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
int ModApiMainMenu::l_check_mod_configuration(lua_State *L)
|
||||||
|
{
|
||||||
|
std::string worldpath = luaL_checkstring(L, 1);
|
||||||
|
|
||||||
|
ModConfiguration modmgr;
|
||||||
|
|
||||||
|
// Add all game mods
|
||||||
|
SubgameSpec gamespec = findWorldSubgame(worldpath);
|
||||||
|
modmgr.addGameMods(gamespec);
|
||||||
|
modmgr.addModsInPath(worldpath + DIR_DELIM + "worldmods", "worldmods");
|
||||||
|
|
||||||
|
// Add user-configured mods
|
||||||
|
std::vector<ModSpec> modSpecs;
|
||||||
|
|
||||||
|
luaL_checktype(L, 2, LUA_TTABLE);
|
||||||
|
|
||||||
|
lua_pushnil(L);
|
||||||
|
while (lua_next(L, 2)) {
|
||||||
|
// Ignore non-string keys
|
||||||
|
if (lua_type(L, -2) != LUA_TSTRING) {
|
||||||
|
throw LuaError(
|
||||||
|
"Unexpected non-string key in table passed to "
|
||||||
|
"core.check_mod_configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string modpath = luaL_checkstring(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
std::string virtual_path = lua_tostring(L, -1);
|
||||||
|
|
||||||
|
modSpecs.emplace_back();
|
||||||
|
ModSpec &spec = modSpecs.back();
|
||||||
|
spec.name = fs::GetFilenameFromPath(modpath.c_str());
|
||||||
|
spec.path = modpath;
|
||||||
|
spec.virtual_path = virtual_path;
|
||||||
|
if (!parseModContents(spec)) {
|
||||||
|
throw LuaError("Not a mod!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modmgr.addMods(modSpecs);
|
||||||
|
try {
|
||||||
|
modmgr.checkConflictsAndDeps();
|
||||||
|
} catch (const ModError &err) {
|
||||||
|
errorstream << err.what() << std::endl;
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
lua_pushboolean(L, false);
|
||||||
|
lua_setfield(L, -2, "is_consistent");
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
lua_setfield(L, -2, "unsatisfied_mods");
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
lua_setfield(L, -2, "satisfied_mods");
|
||||||
|
|
||||||
|
lua_pushstring(L, err.what());
|
||||||
|
lua_setfield(L, -2, "error_message");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
lua_pushboolean(L, modmgr.isConsistent());
|
||||||
|
lua_setfield(L, -2, "is_consistent");
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
int top = lua_gettop(L);
|
||||||
|
unsigned int index = 1;
|
||||||
|
for (const auto &spec : modmgr.getUnsatisfiedMods()) {
|
||||||
|
lua_pushnumber(L, index);
|
||||||
|
push_mod_spec(L, spec, true);
|
||||||
|
lua_settable(L, top);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_setfield(L, -2, "unsatisfied_mods");
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
top = lua_gettop(L);
|
||||||
|
index = 1;
|
||||||
|
for (const auto &spec : modmgr.getMods()) {
|
||||||
|
lua_pushnumber(L, index);
|
||||||
|
push_mod_spec(L, spec, false);
|
||||||
|
lua_settable(L, top);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
lua_setfield(L, -2, "satisfied_mods");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
int ModApiMainMenu::l_show_keys_menu(lua_State *L)
|
int ModApiMainMenu::l_show_keys_menu(lua_State *L)
|
||||||
{
|
{
|
||||||
@ -921,6 +1015,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
|
|||||||
API_FCT(get_worlds);
|
API_FCT(get_worlds);
|
||||||
API_FCT(get_games);
|
API_FCT(get_games);
|
||||||
API_FCT(get_content_info);
|
API_FCT(get_content_info);
|
||||||
|
API_FCT(check_mod_configuration);
|
||||||
API_FCT(start);
|
API_FCT(start);
|
||||||
API_FCT(close);
|
API_FCT(close);
|
||||||
API_FCT(show_keys_menu);
|
API_FCT(show_keys_menu);
|
||||||
|
@ -82,6 +82,8 @@ private:
|
|||||||
|
|
||||||
static int l_get_content_info(lua_State *L);
|
static int l_get_content_info(lua_State *L);
|
||||||
|
|
||||||
|
static int l_check_mod_configuration(lua_State *L);
|
||||||
|
|
||||||
//gui
|
//gui
|
||||||
|
|
||||||
static int l_show_keys_menu(lua_State *L);
|
static int l_show_keys_menu(lua_State *L);
|
||||||
|
BIN
textures/base/pack/checkbox_16_white.png
Normal file
BIN
textures/base/pack/checkbox_16_white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 173 B |
BIN
textures/base/pack/error_icon_orange.png
Normal file
BIN
textures/base/pack/error_icon_orange.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 133 B |
BIN
textures/base/pack/error_icon_red.png
Normal file
BIN
textures/base/pack/error_icon_red.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 133 B |
Loading…
Reference in New Issue
Block a user