diff --git a/.gitignore b/.gitignore index 5c8a6ca..ea6fef4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,10 @@ !.github/ !.github/workflows + !src/ +!src/config/ +!src/config/mapping !.editorconfig !openttd.cfg diff --git a/src/config/config_type.v b/src/config/config_type.v new file mode 100644 index 0000000..adbecff --- /dev/null +++ b/src/config/config_type.v @@ -0,0 +1,19 @@ +module config + +import config.mapping + +pub enum ConfigType { + openttd + private + secrets + // Other config files are not handled by this tool + // because they are not useful for dedicated servers. +} + +fn (ct ConfigType) mapping() mapping.Mapping { + return match ct { + .openttd { mapping.openttd_config } + .private { mapping.private_config } + .secrets { mapping.secrets_config } + } +} diff --git a/src/config/configs.v b/src/config/configs.v new file mode 100644 index 0000000..5e2a4a0 --- /dev/null +++ b/src/config/configs.v @@ -0,0 +1,68 @@ +module config + +import strings + +[noinit] +pub struct OpenTTDConfig { + content map[string]map[string]string + @type ConfigType +} + +pub fn (oc OpenTTDConfig) str() string { + mut config_string := strings.new_builder(12800) + + for section, section_content in oc.content { + config_string.write_string('[${section}]\n') + + for field, value in section_content { + config_string.write_string('${field} = ${value}\n') + } + } + + return config_string.str() +} + +pub struct ManageTTDConfig { + content map[string]map[string]string + @type ConfigType +} + +// TODO: validate values. +pub fn (mc ManageTTDConfig) validate() ! { + mapping := mc.@type.mapping() + + for section, section_content in mc.content { + if section !in mapping { + return error('Invalid section: ${section}') + } + + for field, _ in section_content { + if field !in mapping[section] { + return error('Invalid key: ${field}') + } + } + } +} + +pub fn (mc ManageTTDConfig) to_openttd_config() !OpenTTDConfig { + mc.validate()! + + mapping := mc.@type.mapping() + + mut openttd_config := map[string]map[string]string{} + + for section, section_content in mc.content { + section_mapping := mapping[section] + + for field, value in section_content { + key_mapping := section_mapping[field] + + openttd_config[key_mapping.section()][key_mapping.field()] = key_mapping.transform(value) + } + } + + return OpenTTDConfig{ + content: openttd_config + @type: mc.@type + } +} diff --git a/src/config/mapping/mapping.v b/src/config/mapping/mapping.v new file mode 100644 index 0000000..3f18c8e --- /dev/null +++ b/src/config/mapping/mapping.v @@ -0,0 +1,54 @@ +module mapping + +import config + +pub type Mapping = map[string]map[string]Map + +pub fn (m Mapping) to_managettd_config() config.ManageTTDConfig { + mut config_map := map[string]map[string]string{} + + for section, fields in m { + config_map[section] = map[string]string{} + + for field, map in fields { + config_map[section][field] = map.transformed_default_value() + } + } + + return config.ManageTTDConfig{ + content: config_map + @type: // TODO + } +} + +[noinit] +pub struct Map { + equivalent []string [required] + documentation string [required] + default_value string [required] +pub: + transform fn (string) string = fn (s string) string { + return s + } +} + +pub fn (m Map) section() string { + return m.equivalent[0] +} + +pub fn (m Map) field() string { + return m.equivalent[1] +} + +pub fn (m Map) transformed_default_value() string { + return m.transform(m.default_value) +} + +pub fn (m Map) str() string { + comment := '; ' + m.documentation.split_into_lines().join('; ') + + field := m.field() + value := m.transformed_default_value() + + return '${comment}\n${field} = ${value}' +} diff --git a/src/config/mapping/mappings.v b/src/config/mapping/mappings.v new file mode 100644 index 0000000..24eade8 --- /dev/null +++ b/src/config/mapping/mappings.v @@ -0,0 +1,56 @@ +module mapping + +pub const openttd_config = Mapping({ + 'ratelimits': { + 'maximum_initialization_time': Map{ + equivalent: 'network.max_init_time' + documentation: 'The time the client has to initialize the game in seconds.' + default_value: '5' + transform: seconds_to_ticks + } + 'maximum_join_time': Map{ + equivalent: 'network.max_join_time' + documentation: 'The time the client has to join the server in seconds.' + default_value: '20' + transform: seconds_to_ticks + } + 'maximum_download_time': Map{ + equivalent: 'network.max_download_time' + documentation: 'The time the client has to download the map in seconds.' + default_value: '300' + transform: seconds_to_ticks + } + 'maximum_password_time': Map{ + equivalent: 'network.max_password_time' + documentation: 'The time the client has to enter the server password in seconds.' + default_value: '600' + transform: seconds_to_ticks + } + 'maximum_lag_time': Map{ + equivalent: 'network.max_lag_time' + documentation: 'The time the client can lag behind the server in seconds.' + default_value: '20' + transform: seconds_to_ticks + } + } + 'server': { + 'is_public': Map{ + equivalent: 'network.server_game_type' + documentation: 'Whether if the server is public. True means the server can be joined from the server list.' + default_value: 'true' + transform: boolean_to_custom_value(['private', 'public']) + } + } + 'autoclean': { + 'enabled': Map{ + equivalent: 'network.autoclean_companies' + documentation: 'Whether if the server should automatically clean up companies. +Other autoclean settings only have effect if this is set to true.' + default_value: 'false' + } + } +}) + +pub const private_config = Mapping{} + +pub const secrets_config = Mapping{} diff --git a/openttd.cfg b/src/config/mapping/openttd.cfg similarity index 99% rename from openttd.cfg rename to src/config/mapping/openttd.cfg index 2d18ce2..c9261ce 100644 --- a/openttd.cfg +++ b/src/config/mapping/openttd.cfg @@ -18,7 +18,7 @@ pause_on_join = true server_port = 3979 server_admin_port = 3977 server_admin_chat = true -server_game_type = local +;server_game_type = local autoclean_companies = false autoclean_unprotected = 12 autoclean_protected = 36 diff --git a/src/config/mapping/transformers.v b/src/config/mapping/transformers.v new file mode 100644 index 0000000..9f088ee --- /dev/null +++ b/src/config/mapping/transformers.v @@ -0,0 +1,12 @@ +module mapping + +fn seconds_to_ticks(s string) string { + return (s.int() * 1000 / 30).str() +} + +fn boolean_to_custom_value(values []string) fn (string) string { + // [false, true] + return fn [values] (s string) string { + return values[usize(s.to_lower().bool())] + } +} diff --git a/src/main.v b/src/main.v index edf2574..75ffa9f 100644 --- a/src/main.v +++ b/src/main.v @@ -1,5 +1,5 @@ module main fn main() { - println('Hello World!') + println() } diff --git a/src/mappings.v b/src/mappings.v deleted file mode 100644 index 4e9fad5..0000000 --- a/src/mappings.v +++ /dev/null @@ -1,65 +0,0 @@ -module main - -type MappingValue = Comment | Field - -[params] -struct Field { - // The name of the field in openttd.cfg file. - // This is the thing OpenTTD seees. - name string [required] - // The docs for the field. - docs string [required] - // The value of the field. - value string [required] - // Transforms the value. - transformer fn (string) string = fn (s string) string { - return s - } -} - -type Comment = string - -fn field(f Field) MappingValue { - return f -} - -fn comment(c Comment) MappingValue { - return c -} - -fn seconds_to_ticks(s string) string { - return (s.int() * 1000 / 30).str() -} - -const mappings = { - 'time_limits.maximum_initialization_time': field( - name: 'network.max_init_time' - docs: 'The time the client has to initialize the game in seconds.' - value: '5' - transformer: seconds_to_ticks - ) - 'time_limits.maximum_join_time': field( - name: 'network.max_join_time' - docs: 'The time the client has to join the server in seconds.' - value: '20' - transformer: seconds_to_ticks - ) - 'time_limits.maximum_download_time': field( - name: 'network.max_download_time' - docs: 'The time the client has to download the map in seconds.' - value: '300' - transformer: seconds_to_ticks - ) - 'time_limits.maximum_password_time': field( - name: 'network.max_password_time' - docs: 'The time the client has to enter the server password in seconds.' - value: '600' - transformer: seconds_to_ticks - ) - 'time_limits.maximum_lag_time': field( - name: 'network.max_lag_time' - docs: 'The time the client can lag behind the server in seconds.' - value: '20' - transformer: seconds_to_ticks - ) -}