forked from ab9rf/dfhack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript-manager.lua
More file actions
179 lines (159 loc) · 6.09 KB
/
Copy pathscript-manager.lua
File metadata and controls
179 lines (159 loc) · 6.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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, preprocess_script_file_fn)
for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do
local files = dfhack.filesystem.listdir_recursive(
script_path, nil, false)
if not files then goto skip_path end
for _,f in ipairs(files) do
if f.isdir or not f.path:endswith('.lua') or
f.path:startswith('.git') or
f.path:startswith('test/') or
f.path:startswith('internal/') then
goto continue
end
if preprocess_script_file_fn then
preprocess_script_file_fn(script_path, f.path)
end
local script_name = f.path:sub(1, #f.path - 4) -- remove '.lua'
local ok, script_env = pcall(reqscript, script_name)
if ok then
cb(script_name, script_env)
end
::continue::
end
::skip_path::
end
end
local enabled_map = nil
local function process_script(env_name, env)
local global_name = 'isEnabled'
local fn = env[global_name]
if not fn then return end
if type(fn) ~= 'function' then
dfhack.printerr(
('error registering %s() from "%s": global' ..
' value is not a function'):format(global_name, env_name))
return
end
enabled_map[env_name] = fn
end
function reload(refresh_active_mod_scripts)
enabled_map = utils.OrderedTable()
local force_refresh_fn = refresh_active_mod_scripts and function(script_path, script_name)
if script_path:find('scripts_modactive') then
internal_script = dfhack.internal.scripts[script_path..'/'..script_name]
if internal_script then
internal_script.env = nil
end
end
end or nil
foreach_module_script(process_script, force_refresh_fn)
end
local function ensure_loaded()
if not enabled_map then
reload()
end
end
function list()
ensure_loaded()
for name,fn in pairs(enabled_map) do
print(('%21s %-3s'):format(name..':', fn() and 'on' or 'off'))
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 mod scripts: ' .. 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.value)
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