Ver código fonte

refactor: config-importer.tscn

LanzaSchneider 1 ano atrás
pai
commit
b22df72977
3 arquivos alterados com 147 adições e 13 exclusões
  1. 0 2
      components/editor/.gitignore
  2. 60 0
      editor/config-importer.md
  3. 87 11
      editor/config-importer.tscn

+ 0 - 2
components/editor/.gitignore

@@ -1,2 +0,0 @@
-*.ini
-!.gitignore

+ 60 - 0
editor/config-importer.md

@@ -0,0 +1,60 @@
+# 配置导入工具
+
+此工具可以导入配置表为 ini 文件
+
+## 配置表格式
+
+配置表采用 *.csv 格式,前三行都是表头
+
+### 字段标识符
+
+此字段列的标识符
+尽可能使用 ASCII 字符组成,但不可包含空白含义的字符(如空格符,换行符,制表符等)
+
+### 字段注释
+
+此字段的注释
+
+### 字段说明
+
+由多个部分组成
+
+* 数据类型说明
+
+    格式为 ```:数据类型```
+
+    如 :int 则为整数类型,所有类型详见下表
+
+    | 标识 | 说明 |
+    |---|---|
+    | int | 整数类型 |
+    | float | 浮点类型 |
+    | string | 字符串类型 |
+    | text | 文本类型,和字符串类型的不同在于,它是会自动处理成本地化表项的 |
+    | enum | 枚举类型,具体的枚举规则要看补充说明 |
+    | id | 数据主键类型 |
+    | id_name | 数据主键引用名类型,能够在引用时解析为 id |
+
+    如果最后追加 ```[]``` 则可以形成列表类型
+
+* 数据类型补充说明
+
+    格式为 ```:数据类型,补充说明```
+
+    某些数据类型支持进一步的补充说明,详见下表
+
+    | 补充说明 | 适用的数据类型 | 说明 |
+    |---|---|---|
+    | auto | enum | 自动枚举项,会将配置数据中所有此列出现过的值都归并为枚举值 |
+
+* 引用说明
+
+    格式为 ```@表名```
+
+    表示这个字段是引用某个表中的主键,可以用 ```:id_name``` 配置的引用名
+
+* 默认值说明
+
+    格式为 ```=默认值```
+
+    说明这个配置列的默认值,配置了默认值的表项,在没有配置该值的条目中,就会被忽略

+ 87 - 11
editor/config-importer.tscn

@@ -7,6 +7,7 @@ extends Control
 var path: String
 var _config_path: String
 var _config := ConfigFile.new()
+var _task: Thread
 
 const _config_section := 'CONFIG'
 
@@ -14,26 +15,93 @@ func _enter_tree() -> void:
 	_config_path = path.path_join('config-importer.ini')
 	_config.load(_config_path)
 	$config_path.text = _config.get_value(_config_section, 'config_path', '')
+	$output_path.text = _config.get_value(_config_section, 'output_path', '')
+	$generate_field_info.button_pressed = _config.get_value(_config_section, 'generate_field_info', true)
 
 func _exit_tree() -> void:
 	_config_save()
 
 func _config_save() -> void:
 	_config.set_value(_config_section, 'config_path', $config_path.text)
+	_config.set_value(_config_section, 'output_path', $output_path.text)
+	_config.set_value(_config_section, 'generate_field_info', $generate_field_info.button_pressed)
 	_config.save(_config_path)
 
-func _on_import_perform_button_down() -> void:
-	var dir_access := DirAccess.open($config_path.text)
-	for config_type in dir_access.get_directories():
-		var config_type_dir: String = $config_path.text.path_join(config_type)
-		var config_dir_access := DirAccess.open(config_type_dir)
-		for config_file in config_dir_access.get_files():
-			if config_file.get_extension() != 'csv':
+func _process(delta) -> void:
+	if _task == null || !_task.is_alive():
+		if _task != null && !_task.is_alive():
+			_task.wait_to_finish()
+		_task = null
+		scale = Vector2.ONE
+
+func _setup_task(task:Callable) -> void:
+	scale = Vector2.ZERO
+	_task = Thread.new()
+	_task.start(task)
+
+class ConfigField:
+	var column_id: int
+	var name: String
+	var comment: String
+	var default_record: String
+
+	static func parse(file_access:FileAccess):
+		var fields := []
+		var names := file_access.get_csv_line()
+		var comments := file_access.get_csv_line()
+		var infos := file_access.get_csv_line()
+		for i in range(0, len(names)):
+			if (names[i].is_empty()):
 				continue
-			var file_access := FileAccess.open(config_type_dir.path_join(config_file), FileAccess.READ)
-			while !file_access.eof_reached():
-				var line := file_access.get_csv_line()
-				print(line)
+			var field := ConfigField.new()
+			field.column_id = i
+			field.name = names[i]
+			field.comment = comments[i]
+			fields.push_back(field)
+		return fields
+
+	static func pick(fields, id):
+		for field in fields:
+			if field.name == id:
+				return field
+		return null
+
+func _on_import_perform_button_down() -> void:
+	var full_output_path := ProjectSettings.globalize_path($output_path.text)
+	OS.move_to_trash(full_output_path)
+	DirAccess.make_dir_absolute(full_output_path)
+	_setup_task(func():
+		var dir_access := DirAccess.open($config_path.text)
+		for config_type in dir_access.get_directories():
+			var config_type_dir: String = $config_path.text.path_join(config_type)
+			var config_dir_access := DirAccess.open(config_type_dir)
+			var ini := FileAccess.open($output_path.text.path_join(config_type) + '.ini', FileAccess.WRITE)
+			for config_file in config_dir_access.get_files():
+				if config_file.get_extension() != 'csv':
+					continue
+				var file_access := FileAccess.open(config_type_dir.path_join(config_file), FileAccess.READ)
+				var fields = ConfigField.parse(file_access)
+				var id_field = ConfigField.pick(fields, 'Id')
+				var id_name_field = ConfigField.pick(fields, 'IdName')
+				fields.erase(id_field)
+				fields.erase(id_name_field)
+				ini.store_line('; --------------- {0} ---------------'.format([config_file.get_basename()]))
+				if $generate_field_info.button_pressed:
+					ini.store_line('; {0} data fields:'.format([config_file.get_basename()]))
+					for field in fields:
+						ini.store_line('; {0}={1} ; {2}'.format([field.name, field.default_record, field.comment]))
+					ini.store_line('')
+				while !file_access.eof_reached():
+					var line := file_access.get_csv_line()
+					ini.store_line('[{0}]'.format([line[id_field.column_id]]))
+					for field in fields:
+						var i:int = field.column_id
+						var is_use_default := line[i].is_empty()
+						if is_use_default:
+							continue
+						ini.store_line('{0}={1}'.format([field.name, line[field.column_id]]))
+					ini.store_line('')
+	)
 "
 
 [node name="配置" type="VBoxContainer"]
@@ -48,6 +116,14 @@ script = SubResource("GDScript_d7xax")
 layout_mode = 2
 placeholder_text = "配置路径"
 
+[node name="output_path" type="LineEdit" parent="."]
+layout_mode = 2
+placeholder_text = "生成路径"
+
+[node name="generate_field_info" type="CheckBox" parent="."]
+layout_mode = 2
+text = "生成所有字段注释"
+
 [node name="import_perform" type="Button" parent="."]
 layout_mode = 2
 text = "读取表格生成配置"