17
17
18
18
local FILENAME = " settingtypes.txt"
19
19
20
- local function parse_setting_line (settings , line )
20
+ local CHAR_CLASSES = {
21
+ SPACE = " [%s]" ,
22
+ VARIABLE = " [%w_%-%.]" ,
23
+ INTEGER = " [-]?[%d]" ,
24
+ FLOAT = " [-]?[%d%.]" ,
25
+ FLAGS = " [%w_%-%.,]" ,
26
+ }
27
+
28
+ -- returns error message, or nil
29
+ local function parse_setting_line (settings , line , read_all , base_level )
21
30
-- empty lines
22
- if line :match (" ^[%s] *$" ) then
31
+ if line :match (" ^" .. CHAR_CLASSES . SPACE .. " *$" ) then
23
32
-- clear current_comment so only comments directly above a setting are bound to it
24
33
settings .current_comment = " "
25
34
return
26
35
end
27
36
28
37
-- category
29
- local category = line :match (" ^%[([^%]]+)%]$" )
38
+ local stars , category = line :match (" ^%[([%*]*) ([^%]]+)%]$" )
30
39
if category then
31
- local level = 0
32
- local index = 1
33
- while category :sub (index , index ) == " *" do
34
- level = level + 1
35
- index = index + 1
36
- end
37
- category = category :sub (index , - 1 )
38
40
table.insert (settings , {
39
41
name = category ,
40
- level = level ,
42
+ level = stars : len () + base_level ,
41
43
type = " category" ,
42
44
})
43
45
return
44
46
end
45
47
46
48
-- comment
47
- local comment = line :match (" ^#[%s] *(.*)$" )
49
+ local comment = line :match (" ^#" .. CHAR_CLASSES . SPACE .. " *(.*)$" )
48
50
if comment then
49
51
if settings .current_comment == " " then
50
52
settings .current_comment = comment
@@ -54,9 +56,20 @@ local function parse_setting_line(settings, line)
54
56
return
55
57
end
56
58
59
+ local error_msg
60
+
57
61
-- settings
58
- local first_part , name , readable_name , setting_type =
59
- line :match (" ^(([%w%._-]+)[%s]+%(([^%)]*)%)[%s]+([%w_]+)[%s]*)" )
62
+ local first_part , name , readable_name , setting_type = line :match (" ^"
63
+ -- this first capture group matches the whole first part,
64
+ -- so we can later strip it from the rest of the line
65
+ .. " ("
66
+ .. " ([" .. CHAR_CLASSES .VARIABLE .. " +)" -- variable name
67
+ .. CHAR_CLASSES .SPACE
68
+ .. " %(([^%)]*)%)" -- readable name
69
+ .. CHAR_CLASSES .SPACE
70
+ .. " (" .. CHAR_CLASSES .VARIABLE .. " +)" -- type
71
+ .. CHAR_CLASSES .SPACE .. " ?"
72
+ .. " )" )
60
73
61
74
if first_part then
62
75
if readable_name == " " then
@@ -65,14 +78,15 @@ local function parse_setting_line(settings, line)
65
78
local remaining_line = line :sub (first_part :len () + 1 )
66
79
67
80
if setting_type == " int" then
68
- local default , min , max = remaining_line :match (" ^([%d]+)[%s]*([%d]*)[%s]*([%d]*)$" )
81
+ local default , min , max = remaining_line :match (" ^"
82
+ -- first int is required, the last 2 are optional
83
+ .. " (" .. CHAR_CLASSES .INTEGER .. " +)" .. CHAR_CLASSES .SPACE .. " ?"
84
+ .. " (" .. CHAR_CLASSES .INTEGER .. " *)" .. CHAR_CLASSES .SPACE .. " ?"
85
+ .. " (" .. CHAR_CLASSES .INTEGER .. " *)"
86
+ .. " $" )
69
87
if default and tonumber (default ) then
70
- if min == " " then
71
- min = nil
72
- end
73
- if max == " " then
74
- max = nil
75
- end
88
+ min = tonumber (min )
89
+ max = tonumber (max )
76
90
table.insert (settings , {
77
91
name = name ,
78
92
readable_name = readable_name ,
@@ -83,21 +97,24 @@ local function parse_setting_line(settings, line)
83
97
comment = settings .current_comment ,
84
98
})
85
99
else
86
- core . log ( " error " , " Found invalid int in " .. FILENAME .. " : " .. line )
100
+ error_msg = " Invalid integer setting "
87
101
end
88
102
89
- elseif setting_type == " string" or setting_type == " flags" or setting_type == " noise_params" then
90
- local default = remaining_line :match (" ^[%s]*(.*)$" )
103
+ elseif setting_type == " string" or setting_type == " noise_params"
104
+ or setting_type == " key" then
105
+ local default = remaining_line :match (" ^(.*)$" )
91
106
if default then
92
- table.insert (settings , {
93
- name = name ,
94
- readable_name = readable_name ,
95
- type = setting_type ,
96
- default = default ,
97
- comment = settings .current_comment ,
98
- })
107
+ if setting_type ~= " key" or read_all then -- ignore key type if read_all is false
108
+ table.insert (settings , {
109
+ name = name ,
110
+ readable_name = readable_name ,
111
+ type = setting_type ,
112
+ default = default ,
113
+ comment = settings .current_comment ,
114
+ })
115
+ end
99
116
else
100
- core . log ( " error " , " Found invalid string in " .. FILENAME .. " : " .. line )
117
+ error_msg = " Invalid string setting "
101
118
end
102
119
103
120
elseif setting_type == " bool" then
@@ -110,19 +127,19 @@ local function parse_setting_line(settings, line)
110
127
comment = settings .current_comment ,
111
128
})
112
129
else
113
- core . log ( " error " , " Found invalid bool in " .. FILENAME .. " : " .. line )
130
+ error_msg = " Invalid boolean setting "
114
131
end
115
132
116
133
elseif setting_type == " float" then
117
- local default , min , max
118
- = remaining_line :match (" ^([%d%.]+)[%s]*([%d%.]*)[%s]*([%d%.]*)$" )
134
+ local default , min , max = remaining_line :match (" ^"
135
+ -- first float is required, the last 2 are optional
136
+ .. " (" .. CHAR_CLASSES .FLOAT .. " +)" .. CHAR_CLASSES .SPACE .. " ?"
137
+ .. " (" .. CHAR_CLASSES .FLOAT .. " *)" .. CHAR_CLASSES .SPACE .. " ?"
138
+ .. " (" .. CHAR_CLASSES .FLOAT .. " *)"
139
+ .. " $" )
119
140
if default and tonumber (default ) then
120
- if min == " " then
121
- min = nil
122
- end
123
- if max == " " then
124
- max = nil
125
- end
141
+ min = tonumber (min )
142
+ max = tonumber (max )
126
143
table.insert (settings , {
127
144
name = name ,
128
145
readable_name = readable_name ,
@@ -133,26 +150,26 @@ local function parse_setting_line(settings, line)
133
150
comment = settings .current_comment ,
134
151
})
135
152
else
136
- core . log ( " error " , " Found invalid float in " .. FILENAME .. " : " .. line )
153
+ error_msg = " Invalid float setting "
137
154
end
138
155
139
156
elseif setting_type == " enum" then
140
- local default , values = remaining_line :match (" ^([^%s]+)[%s]+ (.+)$" )
157
+ local default , values = remaining_line :match (" ^(.+) " .. CHAR_CLASSES . SPACE .. " (.+)$" )
141
158
if default and values ~= " " then
142
159
table.insert (settings , {
143
160
name = name ,
144
161
readable_name = readable_name ,
145
162
type = " enum" ,
146
163
default = default ,
147
- values = values :split (" ," ),
164
+ values = values :split (" ," , true ),
148
165
comment = settings .current_comment ,
149
166
})
150
167
else
151
- core . log ( " error " , " Found invalid enum in " .. FILENAME .. " : " .. line )
168
+ error_msg = " Invalid enum setting "
152
169
end
153
170
154
171
elseif setting_type == " path" then
155
- local default = remaining_line :match (" ^[%s]* (.*)$" )
172
+ local default = remaining_line :match (" ^(.*)$" )
156
173
if default then
157
174
table.insert (settings , {
158
175
name = name ,
@@ -162,50 +179,143 @@ local function parse_setting_line(settings, line)
162
179
comment = settings .current_comment ,
163
180
})
164
181
else
165
- core . log ( " error " , " Found invalid path in " .. FILENAME .. " : " .. line )
182
+ error_msg = " Invalid path setting "
166
183
end
167
184
168
- elseif setting_type == " key" then
169
- -- ignore keys, since we have a special dialog for them
185
+ elseif setting_type == " flags" then
186
+ local default , possible = remaining_line :match (" ^"
187
+ .. " (" .. CHAR_CLASSES .FLAGS .. " +)" .. CHAR_CLASSES .SPACE .. " "
188
+ .. " (" .. CHAR_CLASSES .FLAGS .. " +)"
189
+ .. " $" )
190
+ if default and possible then
191
+ table.insert (settings , {
192
+ name = name ,
193
+ readable_name = readable_name ,
194
+ type = " flags" ,
195
+ default = default ,
196
+ possible = possible ,
197
+ comment = settings .current_comment ,
198
+ })
199
+ else
200
+ error_msg = " Invalid flags setting"
201
+ end
170
202
171
203
-- TODO: flags, noise_params (, struct)
172
204
173
205
else
174
- core . log ( " error " , " Found setting with invalid setting type in " .. FILENAME .. " : " .. line )
206
+ error_msg = " Invalid setting type \" " .. type .. " \" "
175
207
end
176
208
else
177
- core . log ( " error " , " Found invalid line in " .. FILENAME .. " : " .. line )
209
+ error_msg = " Invalid line"
178
210
end
179
211
-- clear current_comment since we just used it
180
212
-- if we not just used it, then clear it since we only want comments
181
213
-- directly above the setting to be bound to it
182
214
settings .current_comment = " "
183
- end
184
215
185
- local function parse_config_file ()
186
- local file = io.open (core .get_builtin_path () .. DIR_DELIM .. FILENAME , " r" )
187
- local settings = {}
188
- if not file then
189
- core .log (" error" , " Can't load " .. FILENAME )
190
- return settings
191
- end
216
+ return error_msg
217
+ end
192
218
219
+ local function parse_single_file (file , filepath , read_all , result , base_level )
193
220
-- store this helper variable in the table so it's easier to pass to parse_setting_line()
194
- settings .current_comment = " "
221
+ result .current_comment = " "
195
222
196
223
local line = file :read (" *line" )
197
224
while line do
198
- parse_setting_line (settings , line )
225
+ local error_msg = parse_setting_line (result , line , read_all , base_level )
226
+ if error_msg then
227
+ core .log (" error" , error_msg .. " in " .. filepath .. " \" " .. line .. " \" " )
228
+ end
199
229
line = file :read (" *line" )
200
230
end
201
231
202
- settings .current_comment = nil
232
+ result .current_comment = nil
233
+ end
234
+
235
+ -- read_all: whether to ignore certain setting types for GUI or not
236
+ -- parse_mods: whether to parse settingtypes.txt in mods and games
237
+ local function parse_config_file (read_all , parse_mods )
238
+ local builtin_path = core .get_builtin_path () .. DIR_DELIM .. FILENAME
239
+ local file = io.open (builtin_path , " r" )
240
+ local settings = {}
241
+ if not file then
242
+ core .log (" error" , " Can't load " .. FILENAME )
243
+ return settings
244
+ end
245
+
246
+ parse_single_file (file , builtin_path , read_all , settings , 0 )
203
247
204
248
file :close ()
249
+
250
+ if parse_mods then
251
+ -- Parse games
252
+ local games_category_initialized = false
253
+ local index = 1
254
+ local game = gamemgr .get_game (index )
255
+ while game do
256
+ local path = game .path .. DIR_DELIM .. FILENAME
257
+ local file = io.open (path , " r" )
258
+ if file then
259
+ if not games_category_initialized then
260
+ local translation = fgettext_ne (" Games" ), -- not used, but needed for xgettext
261
+ table.insert (settings , {
262
+ name = " Games" ,
263
+ level = 0 ,
264
+ type = " category" ,
265
+ })
266
+ games_category_initialized = true
267
+ end
268
+
269
+ table.insert (settings , {
270
+ name = game .name ,
271
+ level = 1 ,
272
+ type = " category" ,
273
+ })
274
+
275
+ parse_single_file (file , path , read_all , settings , 2 )
276
+
277
+ file :close ()
278
+ end
279
+
280
+ index = index + 1
281
+ game = gamemgr .get_game (index )
282
+ end
283
+
284
+ -- Parse mods
285
+ local mods_category_initialized = false
286
+ local mods = {}
287
+ get_mods (core .get_modpath (), mods )
288
+ for _ , mod in ipairs (mods ) do
289
+ local path = mod .path .. DIR_DELIM .. FILENAME
290
+ local file = io.open (path , " r" )
291
+ if file then
292
+ if not mods_category_initialized then
293
+ local translation = fgettext_ne (" Mods" ), -- not used, but needed for xgettext
294
+ table.insert (settings , {
295
+ name = " Mods" ,
296
+ level = 0 ,
297
+ type = " category" ,
298
+ })
299
+ mods_category_initialized = true
300
+ end
301
+
302
+ table.insert (settings , {
303
+ name = mod .name ,
304
+ level = 1 ,
305
+ type = " category" ,
306
+ })
307
+
308
+ parse_single_file (file , path , read_all , settings , 2 )
309
+
310
+ file :close ()
311
+ end
312
+ end
313
+ end
314
+
205
315
return settings
206
316
end
207
317
208
- local settings = parse_config_file ()
318
+ local settings = parse_config_file (false , true )
209
319
local selected_setting = 1
210
320
211
321
local function get_current_value (setting )
@@ -236,16 +346,28 @@ local function create_change_setting_formspec(dialogdata)
236
346
237
347
local comment_text = " "
238
348
239
- -- fgettext_ne("") doesn't have to return "", according to specification of gettext(3)
240
349
if setting .comment == " " then
241
350
comment_text = fgettext_ne (" (No description of setting given)" )
242
351
else
243
352
comment_text = fgettext_ne (setting .comment )
244
353
end
245
- for _ , comment_line in ipairs (comment_text :split (" \n " )) do
354
+ for _ , comment_line in ipairs (comment_text :split (" \n " , true )) do
246
355
formspec = formspec .. " ," .. core .formspec_escape (comment_line ) .. " ,"
247
356
end
248
357
358
+ if setting .type == " flags" then
359
+ formspec = formspec .. " ,,"
360
+ .. " ," .. fgettext (" Please enter a comma seperated list of flags." ) .. " ,"
361
+ .. " ," .. fgettext (" Possible values are: " )
362
+ .. core .formspec_escape (setting .possible :gsub (" ," , " , " )) .. " ,"
363
+ elseif setting .type == " noise_params" then
364
+ formspec = formspec .. " ,,"
365
+ .. " ," .. fgettext (" Format: <offset>, <scale>, (<spreadX>, <spreadY>, <spreadZ>), <seed>, <octaves>, <persistence>" ) .. " ,"
366
+ .. " ," .. fgettext (" Optionally the lacunarity can be appended with a leading comma." ) .. " ,"
367
+ end
368
+
369
+ formspec = formspec :sub (1 , - 2 ) -- remove trailing comma
370
+
249
371
formspec = formspec .. " ;1]"
250
372
251
373
if setting .type == " bool" then
@@ -256,7 +378,8 @@ local function create_change_setting_formspec(dialogdata)
256
378
selected_index = 1
257
379
end
258
380
formspec = formspec .. " dropdown[0.5,3.5;3,1;dd_setting_value;"
259
- .. fgettext (" Disabled" ) .. " ," .. fgettext (" Enabled" ) .. " ;" .. selected_index .. " ]"
381
+ .. fgettext (" Disabled" ) .. " ," .. fgettext (" Enabled" ) .. " ;"
382
+ .. selected_index .. " ]"
260
383
261
384
elseif setting .type == " enum" then
262
385
local selected_index = 0
@@ -285,8 +408,20 @@ local function create_change_setting_formspec(dialogdata)
285
408
286
409
else
287
410
-- TODO: fancy input for float, int, flags, noise_params
288
- formspec = formspec .. " field[0.5,4;9.5,1;te_setting_value;;"
289
- .. core .formspec_escape (get_current_value (setting )) .. " ]"
411
+ local width = 10
412
+ local text = get_current_value (setting )
413
+ if dialogdata .error_message then
414
+ formspec = formspec .. " tablecolumns[color;text]" ..
415
+ " tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
416
+ " table[5,4;5,1;error_message;#FF0000,"
417
+ .. core .formspec_escape (dialogdata .error_message ) .. " ;0]"
418
+ width = 5
419
+ if dialogdata .entered_text then
420
+ text = dialogdata .entered_text
421
+ end
422
+ end
423
+ formspec = formspec .. " field[0.5,4;" .. width .. " ,1;te_setting_value;;"
424
+ .. core .formspec_escape (text ) .. " ]"
290
425
end
291
426
return formspec
292
427
end
@@ -303,6 +438,52 @@ local function handle_change_setting_buttons(this, fields)
303
438
local new_value = fields [" dd_setting_value" ]
304
439
core .setting_set (setting .name , new_value )
305
440
441
+ elseif setting .type == " int" then
442
+ local new_value = tonumber (fields [" te_setting_value" ])
443
+ if not new_value or math.floor (new_value ) ~= new_value then
444
+ this .data .error_message = fgettext_ne (" Please enter a valid integer." )
445
+ this .data .entered_text = fields [" te_setting_value" ]
446
+ core .update_formspec (this :get_formspec ())
447
+ return true
448
+ end
449
+ if setting .min and new_value < setting .min then
450
+ this .data .error_message = fgettext_ne (" The value must be greater than $1." , setting .min )
451
+ this .data .entered_text = fields [" te_setting_value" ]
452
+ core .update_formspec (this :get_formspec ())
453
+ return true
454
+ end
455
+ if setting .max and new_value > setting .max then
456
+ this .data .error_message = fgettext_ne (" The value must be lower than $1." , setting .max )
457
+ this .data .entered_text = fields [" te_setting_value" ]
458
+ core .update_formspec (this :get_formspec ())
459
+ return true
460
+ end
461
+ core .setting_set (setting .name , new_value )
462
+
463
+ elseif setting .type == " float" then
464
+ local new_value = tonumber (fields [" te_setting_value" ])
465
+ if not new_value then
466
+ this .data .error_message = fgettext_ne (" Please enter a valid number." )
467
+ this .data .entered_text = fields [" te_setting_value" ]
468
+ core .update_formspec (this :get_formspec ())
469
+ return true
470
+ end
471
+ core .setting_set (setting .name , new_value )
472
+
473
+ elseif setting .type == " flags" then
474
+ local new_value = fields [" te_setting_value" ]
475
+ for _ ,value in ipairs (new_value :split (" ," , true )) do
476
+ value = value :trim ()
477
+ if not value :match (CHAR_CLASSES .FLAGS .. " +" )
478
+ or not setting .possible :match (" [,]?" .. value .. " [,]?" ) then
479
+ this .data .error_message = fgettext_ne (" \" " .. value .. " \" is not a valid flag." )
480
+ this .data .entered_text = fields [" te_setting_value" ]
481
+ core .update_formspec (this :get_formspec ())
482
+ return true
483
+ end
484
+ end
485
+ core .setting_set (setting .name , new_value )
486
+
306
487
else
307
488
local new_value = fields [" te_setting_value" ]
308
489
core .setting_set (setting .name , new_value )
@@ -345,7 +526,8 @@ local function create_settings_formspec(tabview, name, tabdata)
345
526
346
527
if entry .type == " category" then
347
528
current_level = entry .level
348
- formspec = formspec .. " #FFFF00," .. current_level .. " ," .. core .formspec_escape (name ) .. " ,,"
529
+ formspec = formspec .. " #FFFF00," .. current_level .. " ," .. fgettext (name ) .. " ,,"
530
+
349
531
elseif entry .type == " bool" then
350
532
local value = get_current_value (entry )
351
533
if core .is_yes (value ) then
@@ -355,6 +537,10 @@ local function create_settings_formspec(tabview, name, tabdata)
355
537
end
356
538
formspec = formspec .. " ," .. (current_level + 1 ) .. " ," .. core .formspec_escape (name ) .. " ,"
357
539
.. value .. " ,"
540
+
541
+ elseif entry .type == " key" then
542
+ -- ignore key settings, since we have a special dialog for them
543
+
358
544
else
359
545
formspec = formspec .. " ," .. (current_level + 1 ) .. " ," .. core .formspec_escape (name ) .. " ,"
360
546
.. core .formspec_escape (get_current_value (entry )) .. " ,"
@@ -431,6 +617,13 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
431
617
return false
432
618
end
433
619
620
+ tab_settings = {
621
+ name = " settings" ,
622
+ caption = fgettext (" Settings" ),
623
+ cbf_formspec = create_settings_formspec ,
624
+ cbf_button_handler = handle_settings_buttons ,
625
+ }
626
+
434
627
local function create_minetest_conf_example ()
435
628
local result = " # This file contains a list of all available settings and their default value for minetest.conf\n " ..
436
629
" \n " ..
@@ -447,6 +640,7 @@ local function create_minetest_conf_example()
447
640
" # http://wiki.minetest.net/\n " ..
448
641
" \n "
449
642
643
+ local settings = parse_config_file (true , false )
450
644
for _ , entry in ipairs (settings ) do
451
645
if entry .type == " category" then
452
646
if entry .level == 0 then
@@ -458,8 +652,8 @@ local function create_minetest_conf_example()
458
652
result = result .. " # " .. entry .name .. " \n\n "
459
653
end
460
654
else
461
- if entry .comment_line ~= " " then
462
- for _ , comment_line in ipairs (entry .comment :split (" \n " )) do
655
+ if entry .comment ~= " " then
656
+ for _ , comment_line in ipairs (entry .comment :split (" \n " , true )) do
463
657
result = result .. " # " .. comment_line .. " \n "
464
658
end
465
659
end
@@ -473,6 +667,9 @@ local function create_minetest_conf_example()
473
667
if entry .values then
474
668
result = result .. " values: " .. table.concat (entry .values , " , " )
475
669
end
670
+ if entry .possible then
671
+ result = result .. " possible values: " .. entry .possible :gsub (" ," , " , " )
672
+ end
476
673
result = result .. " \n "
477
674
result = result .. " # " .. entry .name .. " = " .. entry .default .. " \n\n "
478
675
end
@@ -485,6 +682,8 @@ local function create_translation_file()
485
682
" // It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files\n " ..
486
683
" // To update it, refer to the bottom of builtin/mainmenu/tab_settings.lua\n\n " ..
487
684
" fake_function() {\n "
685
+
686
+ local settings = parse_config_file (true , false )
488
687
for _ , entry in ipairs (settings ) do
489
688
if entry .type == " category" then
490
689
result = result .. " \t gettext(\" " .. entry .name .. " \" );\n "
@@ -511,16 +710,9 @@ if false then
511
710
end
512
711
513
712
if false then
514
- local file = io.open (" src/settings_translation_file.c " , " w" )
713
+ local file = io.open (" src/settings_translation_file.cpp " , " w" )
515
714
if file then
516
715
file :write (create_translation_file ())
517
716
file :close ()
518
717
end
519
718
end
520
-
521
- tab_settings = {
522
- name = " settings" ,
523
- caption = fgettext (" Settings" ),
524
- cbf_formspec = create_settings_formspec ,
525
- cbf_button_handler = handle_settings_buttons ,
526
- }
0 commit comments