Explorar o código

refactor: 外置配置导入脚本

LanzaSchneider hai 1 ano
pai
achega
d394df1ef6
Modificáronse 2 ficheiros con 122 adicións e 74 borrados
  1. 26 74
      editor/config-importer.tscn
  2. 96 0
      editor/tools/config-importer.rb

+ 26 - 74
editor/config-importer.tscn

@@ -9,6 +9,9 @@ var _config_path: String
 var _config := ConfigFile.new()
 var _task: Thread
 
+@export_file('*.rb')
+var _tool_file: String
+
 const _config_section := 'CONFIG'
 
 func _enter_tree() -> void:
@@ -19,6 +22,7 @@ func _enter_tree() -> void:
 	$config_path.text = _config.get_value(_config_section, 'config_path', '')
 	$output_path.text = _config.get_value(_config_section, 'output_path', '')
 	$generate_code_path.text = _config.get_value(_config_section, 'generate_code_path', '')
+	$generate_text_path.text = _config.get_value(_config_section, 'generate_text_path', '')
 	$generate_field_info.button_pressed = _config.get_value(_config_section, 'generate_field_info', true)
 
 func _exit_tree() -> void:
@@ -30,6 +34,7 @@ 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_code_path', $generate_code_path.text)
+	_config.set_value(_config_section, 'generate_text_path', $generate_text_path.text)
 	_config.set_value(_config_section, 'generate_field_info', $generate_field_info.button_pressed)
 	_config.save(_config_path)
 
@@ -45,90 +50,32 @@ func _setup_task(task:Callable) -> void:
 	_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 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 _recreate_dir(full_path:String) -> void:
 	if DirAccess.dir_exists_absolute(full_path):
 		OS.move_to_trash(full_path)
 	DirAccess.make_dir_recursive_absolute(full_path)
 
 func _on_import_perform_button_down() -> void:
+	_setup_task(perform)
+
+func perform():
 	var full_output_path := ProjectSettings.globalize_path($output_path.text)
 	var full_gen_code_path := ProjectSettings.globalize_path($generate_code_path.text)
-
+	var full_gen_text_path := ProjectSettings.globalize_path($generate_text_path.text)
+	var params: PackedStringArray = [
+		ProjectSettings.globalize_path(_tool_file),
+		ProjectSettings.globalize_path($config_path.text),
+		ProjectSettings.globalize_path($output_path.text),
+		full_gen_code_path,
+		full_gen_text_path,
+		$generate_field_info.button_pressed
+		]
+	for i in range(0, len(params)):
+		params[i] = ('\"' + params[i] + '\"').strip_edges()
 	_recreate_dir(full_output_path)
 	_recreate_dir(full_gen_code_path)
-
-	_setup_task(func():
-		var dir_access := DirAccess.open($config_path.text)
-		var gen_script := FileAccess.open($generate_code_path.text.path_join('config') + '.gen.gd', FileAccess.WRITE)
-		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')
-				var config_title := config_file.get_basename()
-				var config_class_name := config_title.capitalize().replace(' ', '')
-				fields.erase(id_field)
-				fields.erase(id_name_field)
-
-				ini.store_line('; --------------- {0} ---------------'.format([config_title]))
-
-				if $generate_field_info.button_pressed:
-					ini.store_line('; {0} data fields:'.format([config_title]))
-					for field in fields:
-						ini.store_line('; {0}={1} ; {2}'.format([field.name, field.default_record, field.comment]))
-					ini.store_line('')
-
-				gen_script.store_line('class {0}:'.format([config_class_name]))
-				for field in fields:
-					gen_script.store_line('\\t# {0}'.format([field.name]))
-
-				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('')
-
-				gen_script.store_line('\\tpass')
-				gen_script.store_line('')
-	)
+	_recreate_dir(full_gen_text_path)
+	OS.execute('ruby', params)
 "
 
 [node name="配置" type="VBoxContainer"]
@@ -138,6 +85,7 @@ anchor_bottom = 1.0
 grow_horizontal = 2
 grow_vertical = 2
 script = SubResource("GDScript_d7xax")
+_tool_file = "res://addons/godot-TBL-extension-base/editor/tools/config-importer.rb"
 
 [node name="config_path" type="LineEdit" parent="."]
 layout_mode = 2
@@ -151,6 +99,10 @@ placeholder_text = "ini 生成路径"
 layout_mode = 2
 placeholder_text = "代码生成路径"
 
+[node name="generate_text_path" type="LineEdit" parent="."]
+layout_mode = 2
+placeholder_text = "文本表生成路径"
+
 [node name="generate_field_info" type="CheckBox" parent="."]
 layout_mode = 2
 button_pressed = true

+ 96 - 0
editor/tools/config-importer.rb

@@ -0,0 +1,96 @@
+config_path, ini_path, code_path, text_path, is_generate_field_info = *ARGV
+is_generate_field_info = (is_generate_field_info == 'true')
+
+require 'csv'
+
+def convert_case(source)
+  return source.split('_').collect(&:capitalize).join
+end
+
+# 各类型定义
+TypeInfo = Struct.new(:data_type, :data_type_extra, :ref_info, :default_value)
+FieldInfo = Struct.new(:column_id, :identifier, :comment, :type_info)
+Table = Struct.new(:csv, :fields, :name2id, :id_field)
+Ini = Struct.new(:io, :tables)
+inis = {}
+
+# 读取配置表子目录,以形成配置数据目录
+Dir["#{config_path}/*"].each do |path|
+  ini_name = File::basename(path)
+  ini = (inis[ini_name] ||= Ini.new(File.open("#{ini_path}/#{ini_name}.ini", 'wb'), {}))
+  next unless File::directory?(path)
+  # 每个配置表子目录将成为一个 ini 文件
+  # 内含全部子 .csv 表格的导入数据
+  Dir["#{path}/*.csv"].each do |file|
+    table_name = File::basename(file, '.csv')
+    table = (ini.tables[convert_case(table_name)] = Table.new(csv = CSV.open(file), [], {}, nil))
+    identifiers = csv.readline
+    comments = csv.readline
+    type_infos = csv.readline
+    field_id = nil
+    field_id_name = nil
+    # 处理表头列
+    identifiers.each_with_index do |identifier, index|
+      next if identifier.nil? || identifier.empty?
+      type_info = TypeInfo.new(nil, nil, nil, nil)
+      type_infos[index].gsub(/\s+/, '').scan(/[(:|,|@|=)][a-zA-Z_]+/).each do |info|
+        type_info.data_type = info[1..info.size] if info.start_with?(':')
+        type_info.data_type_extra = info[1..info.size] if info.start_with?(',')
+        type_info.ref_info = info[1..info.size] if info.start_with?('@')
+        type_info.default_value = info[1..info.size] if info.start_with?('=')
+      end
+      field = FieldInfo.new(index, identifier, comments[index], type_info)
+      # 处理特殊类型的字段
+      case type_info.data_type
+      when 'id'
+        field_id = field
+        next
+      when 'id_name'
+        field_id_name = field
+        next
+      end
+      # 参与导入的条目
+      table.fields << field
+    end
+    # 验证 Id 列
+    next if field_id.nil?
+    table.id_field = field_id
+    # 处理条目形成引用表
+    while !csv.eof?
+      line = csv.readline
+      table.name2id[line[field_id_name.column_id]] = line[field_id.column_id].upcase
+    end
+    # 回滚 csv 状态至表头读取后
+    csv.rewind
+    3.times { csv.readline }
+  end
+end
+
+# 导入实际数据条目
+inis.each_pair do |ini_name, ini_data|
+  register_table = {}
+  buffer = StringIO.new
+  ini_data.tables.each_pair do |table_name, table|
+    register = (register_table[table_name] = [])
+    while !table.csv.eof?
+      line = table.csv.readline
+      id = line[table.id_field.column_id].upcase
+      buffer.puts "[#{id}]"
+      table.fields.each do |field|
+        value = line[field.column_id]
+        next if value.nil? || value.empty?
+        buffer.puts "#{field.identifier}=#{value}"
+      end
+      buffer.puts
+      register << id
+    end
+  end
+  File.open("#{ini_path}/#{ini_name}.ini", 'wb') do |ini|
+    ini.puts '[REGISTER]'
+    register_table.each_pair do |table_name, register|
+      ini.puts "#{table_name}=#{register.join(',')}"
+    end
+    ini.puts
+    ini.puts buffer.string
+  end
+end