-
-
Notifications
You must be signed in to change notification settings - Fork 452
/
configuration.dm
414 lines (358 loc) · 13.1 KB
/
configuration.dm
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
/datum/controller/configuration
name = "Configuration"
var/directory = "config"
var/warned_deprecated_configs = FALSE
var/hiding_entries_by_type = TRUE //Set for readability, admins can set this to FALSE if they want to debug it
var/list/entries
var/list/entries_by_type
var/list/maplist
var/datum/map_config/defaultmap
var/list/modes // allowed modes
var/list/gamemode_cache
var/list/votable_modes // votable modes
var/list/mode_names
var/list/mode_reports
var/list/mode_false_report_weight
var/motd
var/policy
/// If the configuration is loaded
var/load_complete = FALSE
/datum/controller/configuration/proc/admin_reload()
if(IsAdminAdvancedProcCall())
return
log_admin("[key_name_admin(usr)] has forcefully reloaded the configuration from disk.")
message_admins("[key_name_admin(usr)] has forcefully reloaded the configuration from disk.")
full_wipe()
Load(world.params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER])
/datum/controller/configuration/proc/Load(_directory)
if(IsAdminAdvancedProcCall()) //If admin proccall is detected down the line it will horribly break everything.
return
if(_directory)
directory = _directory
if(entries)
CRASH("/datum/controller/configuration/Load() called more than once!")
InitEntries()
LoadModes()
if(fexists("[directory]/config.txt") && LoadEntries("config.txt") <= 1)
var/list/legacy_configs = list("game_options.txt", "dbconfig.txt", "comms.txt")
for(var/I in legacy_configs)
if(fexists("[directory]/[I]"))
log_config("No $include directives found in config.txt! Loading legacy [legacy_configs.Join("/")] files...")
for(var/J in legacy_configs)
LoadEntries(J)
break
if (fexists("[directory]/ezdb.txt"))
LoadEntries("ezdb.txt")
loadmaplist(CONFIG_MAPS_FILE)
LoadMOTD()
LoadPolicy()
load_complete = TRUE
if (Master)
Master.OnConfigLoad()
/datum/controller/configuration/proc/full_wipe()
if(IsAdminAdvancedProcCall())
return
entries_by_type.Cut()
QDEL_LIST_ASSOC_VAL(entries)
entries = null
QDEL_LIST_ASSOC_VAL(maplist)
maplist = null
QDEL_NULL(defaultmap)
/datum/controller/configuration/Destroy()
full_wipe()
config = null
return ..()
/datum/controller/configuration/proc/InitEntries()
var/list/_entries = list()
entries = _entries
var/list/_entries_by_type = list()
entries_by_type = _entries_by_type
for(var/I in typesof(/datum/config_entry)) //typesof is faster in this case
var/datum/config_entry/E = I
if(initial(E.abstract_type) == I)
continue
E = new I
var/esname = E.name
var/datum/config_entry/test = _entries[esname]
if(test)
log_config("Error: [test.type] has the same name as [E.type]: [esname]! Not initializing [E.type]!")
qdel(E)
continue
_entries[esname] = E
_entries_by_type[I] = E
/datum/controller/configuration/proc/RemoveEntry(datum/config_entry/CE)
entries -= CE.name
entries_by_type -= CE.type
/datum/controller/configuration/proc/LoadEntries(filename, list/stack = list())
if(IsAdminAdvancedProcCall())
return
var/filename_to_test = world.system_type == MS_WINDOWS ? lowertext(filename) : filename
if(filename_to_test in stack)
log_config("Warning: Config recursion detected ([english_list(stack)]), breaking!")
return
stack = stack + filename_to_test
log_config("Loading config file [filename]...")
var/list/lines = world.file2list("[directory]/[filename]")
var/list/_entries = entries
for(var/L in lines)
L = trim(L)
if(!L)
continue
var/firstchar = L[1]
if(firstchar == "#")
continue
var/lockthis = firstchar == "@"
if(lockthis)
L = copytext(L, length(firstchar) + 1)
var/pos = findtext(L, " ")
var/entry = null
var/value = null
if(pos)
entry = lowertext(copytext(L, 1, pos))
value = copytext(L, pos + length(L[pos]))
else
entry = lowertext(L)
if(!entry)
continue
if(entry == "$include")
if(!value)
log_config("Warning: Invalid $include directive: [value]")
else
LoadEntries(value, stack)
++.
continue
var/datum/config_entry/E = _entries[entry]
if(!E)
log_config("Unknown setting in configuration: '[entry]'")
continue
if(lockthis)
E.protection |= CONFIG_ENTRY_LOCKED
if(E.deprecated_by)
var/datum/config_entry/new_ver = entries_by_type[E.deprecated_by]
var/new_value = E.DeprecationUpdate(value)
var/good_update = istext(new_value)
log_config("Entry [entry] is deprecated and will be removed soon. Migrate to [new_ver.name]![good_update ? " Suggested new value is: [new_value]" : ""]")
if(!warned_deprecated_configs)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(message_admins), "This server is using deprecated configuration settings. Please check the logs and update accordingly."), 0)
warned_deprecated_configs = TRUE
if(good_update)
value = new_value
E = new_ver
else
warning("[new_ver.type] is deprecated but gave no proper return for DeprecationUpdate()")
var/validated = E.ValidateAndSet(value)
if(!validated)
log_config("Failed to validate setting \"[value]\" for [entry]")
else
if(E.modified && !E.dupes_allowed)
log_config("Duplicate setting for [entry] ([value], [E.resident_file]) detected! Using latest.")
E.resident_file = filename
if(validated)
E.modified = TRUE
++.
/datum/controller/configuration/can_vv_get(var_name)
return (var_name != NAMEOF(src, entries_by_type) || !hiding_entries_by_type) && ..()
/datum/controller/configuration/vv_edit_var(var_name, var_value)
var/list/banned_edits = list(NAMEOF(src, entries_by_type), NAMEOF(src, entries), NAMEOF(src, directory))
return !(var_name in banned_edits) && ..()
/datum/controller/configuration/stat_entry(msg)
msg = "Edit"
return msg
/datum/controller/configuration/proc/Get(entry_type)
var/datum/config_entry/E = entry_type
var/entry_is_abstract = initial(E.abstract_type) == entry_type
if(entry_is_abstract)
CRASH("Tried to retrieve an abstract config_entry: [entry_type]")
if(!islist(entries_by_type))
CRASH("entries_by_type is not a list when trying to get: [entry_type]")
E = entries_by_type[entry_type]
if(!E)
CRASH("Missing config entry for [entry_type]!")
if((E.protection & CONFIG_ENTRY_HIDDEN) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]")
return
return E.config_entry_value
/datum/controller/configuration/proc/Set(entry_type, new_val)
var/datum/config_entry/E = entry_type
var/entry_is_abstract = initial(E.abstract_type) == entry_type
if(entry_is_abstract)
CRASH("Tried to set an abstract config_entry: [entry_type]")
E = entries_by_type[entry_type]
if(!E)
CRASH("Missing config entry for [entry_type]!")
if((E.protection & CONFIG_ENTRY_LOCKED) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Set" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
log_admin_private("Config rewrite of [entry_type] to [new_val] attempted by [key_name(usr)]")
return
return E.ValidateAndSet("[new_val]")
/datum/controller/configuration/proc/LoadModes()
gamemode_cache = typecacheof(/datum/game_mode, TRUE)
modes = list()
mode_names = list()
mode_reports = list()
mode_false_report_weight = list()
votable_modes = list()
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
for(var/T in gamemode_cache)
// I wish I didn't have to instance the game modes in order to look up
// their information, but it is the only way (at least that I know of).
var/datum/game_mode/M = new T()
if(M.config_tag)
if(!(M.config_tag in modes)) // ensure each mode is added only once
modes += M.config_tag
mode_names[M.config_tag] = M.name
probabilities[M.config_tag] = M.probability
mode_reports[M.report_type] = M.generate_report()
if(probabilities[M.config_tag]>0)
mode_false_report_weight[M.report_type] = M.false_report_weight
else
//"impossible" modes will still falsly show up occasionally, else they'll stick out like a sore thumb if an admin decides to force a disabled gamemode.
mode_false_report_weight[M.report_type] = min(1, M.false_report_weight)
if(M.votable)
votable_modes += M.config_tag
qdel(M)
votable_modes += "secret"
/datum/controller/configuration/proc/LoadMOTD()
motd = file2text("[directory]/motd.txt")
var/tm_info = GLOB.revdata.GetTestMergeInfo()
if(motd || tm_info)
motd = motd ? "[motd]<br>[tm_info]" : tm_info
/*
Policy file should be a json file with a single object.
Value is raw html.
Possible keywords :
Job titles / Assigned roles (ghost spawners for example) : Assistant , Captain , Ash Walker
Mob types : /mob/living/simple_animal/hostile/carp
Antagonist types : /datum/antagonist/highlander
Species types : /datum/species/lizard
special keywords defined in _DEFINES/admin.dm
Example config:
{
"Assistant" : "Don't kill everyone",
"/datum/antagonist/highlander" : "<b>Kill everyone</b>",
"Ash Walker" : "Kill all spacemans"
}
*/
/datum/controller/configuration/proc/LoadPolicy()
policy = list()
var/rawpolicy = file2text("[directory]/policy.json")
if(rawpolicy)
var/parsed = json_decode(rawpolicy)
if(!parsed)
log_config("JSON parsing failure for policy.json")
else
policy = parsed
/datum/controller/configuration/proc/loadmaplist(filename)
log_config("Loading config file [filename]...")
filename = "[directory]/[filename]"
var/list/Lines = world.file2list(filename)
var/datum/map_config/currentmap = null
for(var/t in Lines)
if(!t)
continue
t = trim(t)
if(length(t) == 0)
continue
else if(t[1] == "#")
continue
var/pos = findtext(t, " ")
var/command = null
var/data = null
if(pos)
command = lowertext(copytext(t, 1, pos))
data = copytext(t, pos + length(t[pos]))
else
command = lowertext(t)
if(!command)
continue
if (!currentmap && command != "map")
continue
switch (command)
if ("map")
currentmap = load_map_config(data, MAP_DIRECTORY_MAPS)
if(currentmap.defaulted)
var/error_message = "Failed to load map config for [data]!"
log_config(error_message)
log_mapping(error_message, TRUE)
currentmap = null
if ("minplayers","minplayer")
currentmap.config_min_users = text2num(data)
if ("maxplayers","maxplayer")
currentmap.config_max_users = text2num(data)
if ("weight","voteweight")
currentmap.voteweight = text2num(data)
if ("default","defaultmap")
defaultmap = currentmap
if ("votable")
currentmap.votable = TRUE
if ("endmap")
LAZYINITLIST(maplist)
maplist[currentmap.map_name] = currentmap
currentmap = null
if ("disabled")
currentmap = null
else
log_config("Unknown command in map vote config: '[command]'")
/datum/controller/configuration/proc/pick_mode(mode_name)
// I wish I didn't have to instance the game modes in order to look up
// their information, but it is the only way (at least that I know of).
// ^ This guy didn't try hard enough
for(var/T in gamemode_cache)
var/datum/game_mode/M = T
var/ct = initial(M.config_tag)
if(ct && ct == mode_name)
return new T
return new /datum/game_mode/extended()
/datum/controller/configuration/proc/get_runnable_modes()
var/list/datum/game_mode/runnable_modes = new
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
var/list/min_pop = Get(/datum/config_entry/keyed_list/min_pop)
var/list/max_pop = Get(/datum/config_entry/keyed_list/max_pop)
var/list/repeated_mode_adjust = Get(/datum/config_entry/number_list/repeated_mode_adjust)
for(var/T in gamemode_cache)
var/datum/game_mode/M = new T()
if(!(M.config_tag in modes))
qdel(M)
continue
if(probabilities[M.config_tag]<=0)
qdel(M)
continue
if(min_pop[M.config_tag])
M.required_players = min_pop[M.config_tag]
if(max_pop[M.config_tag])
M.maximum_players = max_pop[M.config_tag]
if(M.can_start())
var/final_weight = probabilities[M.config_tag]
if(SSpersistence.saved_modes.len == 3 && repeated_mode_adjust.len == 3)
var/recent_round = min(SSpersistence.saved_modes.Find(M.config_tag),3)
var/adjustment = 0
while(recent_round)
adjustment += repeated_mode_adjust[recent_round]
recent_round = SSpersistence.saved_modes.Find(M.config_tag,recent_round+1,0)
final_weight *= ((100-adjustment)/100)
runnable_modes[M] = final_weight
return runnable_modes
/datum/controller/configuration/proc/get_runnable_midround_modes(crew)
var/list/datum/game_mode/runnable_modes = new
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
var/list/min_pop = Get(/datum/config_entry/keyed_list/min_pop)
var/list/max_pop = Get(/datum/config_entry/keyed_list/max_pop)
for(var/T in (gamemode_cache - SSticker.mode.type))
var/datum/game_mode/M = new T()
if(!(M.config_tag in modes))
qdel(M)
continue
if(probabilities[M.config_tag]<=0)
qdel(M)
continue
if(min_pop[M.config_tag])
M.required_players = min_pop[M.config_tag]
if(max_pop[M.config_tag])
M.maximum_players = max_pop[M.config_tag]
if(M.required_players <= crew)
if(M.maximum_players >= 0 && M.maximum_players < crew)
continue
runnable_modes[M] = probabilities[M.config_tag]
return runnable_modes
//Message admins when you can.
/datum/controller/configuration/proc/DelayedMessageAdmins(text)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(message_admins), text), 0)