@ -2,6 +2,9 @@ local _ENV = mkmodule('script-manager')
local utils = require ( ' utils ' )
---------------------
-- enabled API
-- for each script that can be loaded as a module, calls cb(script_name, env)
function foreach_module_script ( cb )
for _ , script_path in ipairs ( dfhack.internal . getScriptPaths ( ) ) do
@ -57,4 +60,107 @@ function list()
end
end
---------------------
-- mod script paths
-- this perhaps could/should be queried from the Steam API
-- are there any installation configurations where this will be wrong, though?
local WORKSHOP_MODS_PATH = ' ../../workshop/content/975370/ '
local MODS_PATH = ' mods/ '
local INSTALLED_MODS_PATH = ' data/installed_mods/ '
-- last instance of the same version of the same mod wins, so read them in this
-- order (in increasing order of liklihood that players may have made custom
-- changes to the files)
local MOD_PATH_ROOTS = { WORKSHOP_MODS_PATH , MODS_PATH , INSTALLED_MODS_PATH }
local function get_mod_id_and_version ( path )
local idfile = path .. ' /info.txt '
local ok , lines = pcall ( io.lines , idfile )
if not ok then return end
local id , version
for line in lines do
if not id then
_ , _ , id = line : find ( ' ^%[ID:([^%]]+)%] ' )
end
if not version then
-- note this doesn't include the closing brace since some people put
-- non-number characters in here, and DF only reads the digits as the
-- numeric version
_ , _ , version = line : find ( ' ^%[NUMERIC_VERSION:(%d+) ' )
end
-- note that we do *not* want to break out of this loop early since
-- lines has to hit EOF to close the file
end
return id , version
end
local function add_script_path ( mod_script_paths , path )
if dfhack.filesystem . isdir ( path ) then
print ( ' indexing scripts from mod script path: ' .. path )
table.insert ( mod_script_paths , path )
end
end
local function add_script_paths ( mod_script_paths , base_path , include_modactive )
if not base_path : endswith ( ' / ' ) then
base_path = base_path .. ' / '
end
if include_modactive then
add_script_path ( mod_script_paths , base_path .. ' scripts_modactive ' )
end
add_script_path ( mod_script_paths , base_path .. ' scripts_modinstalled ' )
end
function get_mod_script_paths ( )
-- ordered map of mod id -> {handled=bool, versions=map of version -> path}
local mods = utils.OrderedTable ( )
local mod_script_paths = { }
-- if a world is loaded, process active mods first, and lock to active version
if dfhack.isWorldLoaded ( ) then
for _ , path in ipairs ( df.global . world.object_loader . object_load_order_src_dir ) do
path = tostring ( path )
if not path : startswith ( INSTALLED_MODS_PATH ) then goto continue end
local id = get_mod_id_and_version ( path )
if not id then goto continue end
mods [ id ] = { handled = true }
add_script_paths ( mod_script_paths , path , true )
:: continue ::
end
end
-- assemble version -> path maps for all (non-handled) mod source dirs
for _ , mod_path_root in ipairs ( MOD_PATH_ROOTS ) do
local files = dfhack.filesystem . listdir_recursive ( mod_path_root , 0 )
if not files then goto skip_path_root end
for _ , f in ipairs ( files ) do
if not f.isdir then goto continue end
local id , version = get_mod_id_and_version ( f.path )
if not id or not version then goto continue end
local mod = ensure_key ( mods , id )
if mod.handled then goto continue end
ensure_key ( mod , ' versions ' ) [ version ] = f.path
:: continue ::
end
:: skip_path_root ::
end
-- add script paths from most recent version of all not-yet-handled mods
for _ , v in pairs ( mods ) do
if v.handled then goto continue end
local max_version , path
for version , mod_path in pairs ( v.versions ) do
if not max_version or max_version < version then
path = mod_path
max_version = version
end
end
add_script_paths ( mod_script_paths , path )
:: continue ::
end
return mod_script_paths
end
return _ENV