Repository: tableauio/tableau
Pull request #1 (2022-02-23 08:29:57)

feat: autogen tag and attr at first-pass scanning and filter xml by `@TABLEAU` keyword
Features:
1. automatically generate tags and attrs not pre-defined in `<@TABLEAU/>` but used later.
2. filter xml files in the directory and only generate protobuf IR for those not having `<@TABLEAU/>` keyword.


Patch:
diff --git a/internal/confgen/confgen.go b/internal/confgen/confgen.go
index 2dfa606..5316122 100644
--- a/internal/confgen/confgen.go
+++ b/internal/confgen/confgen.go
@@ -23,7 +23,7 @@ type Generator struct {
 	OutputDir    string // output dir of generated files.
 
 	Output *options.OutputOption // output settings.
-	Input *options.InputOption // Input settings.
+	Input  *options.InputOption  // Input settings.
 	Header *options.HeaderOption // header settings.
 }
 
@@ -40,12 +40,17 @@ func NewGenerator(protoPackage, indir, outdir string, setters ...options.Option)
 		InputDir:     indir,
 		OutputDir:    outdir,
 		Output:       opts.Output,
-		Input: opts.Input,
-		Header: opts.Header,
+		Input:        opts.Input,
+		Header:       opts.Header,
 	}
 	return g
 }
 
+type sheetInfo struct {
+	MessageName string
+	opts        *tableaupb.WorksheetOptions
+}
+
 func (gen *Generator) Generate(relWorkbookPath string, worksheetName string) (err error) {
 	if relWorkbookPath != "" {
 		relCleanSlashPath := filepath.ToSlash(filepath.Clean(relWorkbookPath))
@@ -80,31 +85,32 @@ func (gen *Generator) Generate(relWorkbookPath string, worksheetName string) (er
 				workbookFound = true
 
 				var sheets []string
-				sheetMap := map[string]string{} // sheet name -> message name
+				// sheet name -> message name
+				sheetMap := map[string]sheetInfo{}
 				msgs := fd.Messages()
 				for i := 0; i < msgs.Len(); i++ {
 					md := msgs.Get(i)
 					opts := md.Options().(*descriptorpb.MessageOptions)
 					worksheet := proto.GetExtension(opts, tableaupb.E_Worksheet).(*tableaupb.WorksheetOptions)
 					if worksheet != nil {
-						sheetMap[worksheet.Name] = string(md.Name())
+						sheetMap[worksheet.Name] = sheetInfo{string(md.Name()), worksheet}
 						sheets = append(sheets, worksheet.Name)
 					}
 				}
 				wbPath := filepath.Join(gen.InputDir, workbook.Name)
 				imp := importer.New(wbPath, importer.Sheets(sheets), importer.Format(gen.Input.Format), importer.Header(gen.Header))
 				// atom.Log.Debugf("proto: %s, workbook %s", fd.Path(), workbook)
-				for sheetName, msgName := range sheetMap {
+				for sheetName, sheetInfo := range sheetMap {
 					if worksheetName != "" && worksheetName != sheetName {
 						continue
 					}
 					worksheetFound = true
 
-					md := msgs.ByName(protoreflect.Name(msgName))
+					md := msgs.ByName(protoreflect.Name(sheetInfo.MessageName))
 					// atom.Log.Debugf("%s", md.FullName())
 					atom.Log.Infof("generate: %s#%s <-> %s#%s", fd.Path(), md.Name(), workbook.Name, sheetName)
 					newMsg := dynamicpb.NewMessage(md)
-					parser := NewSheetParser(gen.ProtoPackage, gen.LocationName)
+					parser := NewSheetParser(gen.ProtoPackage, gen.LocationName, sheetInfo.opts)
 					exporter := NewSheetExporter(gen.OutputDir, gen.Output)
 					err := exporter.Export(imp, parser, newMsg)
 					if err != nil {
diff --git a/internal/confgen/parser.go b/internal/confgen/parser.go
index fab21d8..37fcc5d 100644
--- a/internal/confgen/parser.go
+++ b/internal/confgen/parser.go
@@ -42,7 +42,7 @@ func (x *sheetExporter) Export(imp importer.Importer, parser *sheetParser, proto
 		return errors.WithMessagef(err, "get sheet failed: %s", wsOpts.Name)
 	}
 
-	if err := parser.Parse(protomsg, sheet, wsOpts); err != nil {
+	if err := parser.Parse(protomsg, sheet); err != nil {
 		return errors.WithMessage(err, "failed to parse sheet")
 	}
 
@@ -53,43 +53,45 @@ func (x *sheetExporter) Export(imp importer.Importer, parser *sheetParser, proto
 type sheetParser struct {
 	ProtoPackage string
 	LocationName string
+	opts         *tableaupb.WorksheetOptions
 }
 
-func NewSheetParser(protoPackage, locationName string) *sheetParser {
+func NewSheetParser(protoPackage, locationName string, opts *tableaupb.WorksheetOptions) *sheetParser {
 	return &sheetParser{
 		ProtoPackage: protoPackage,
 		LocationName: locationName,
+		opts:         opts,
 	}
 }
 
-func (sp *sheetParser) Parse(protomsg proto.Message, sheet *importer.Sheet, wsOpts *tableaupb.WorksheetOptions) error {
+func (sp *sheetParser) Parse(protomsg proto.Message, sheet *importer.Sheet) error {
 	// atom.Log.Debugf("parse sheet: %s", sheet.Name)
 	msg := protomsg.ProtoReflect()
-	if wsOpts.Transpose {
+	if sp.opts.Transpose {
 		// interchange the rows and columns
 		// namerow: name column
 		// [datarow, MaxCol]: data column
 		// kvRow := make(map[string]string)
 		var prev *importer.RowCells
-		for col := int(wsOpts.Datarow) - 1; col < sheet.MaxCol; col++ {
+		for col := int(sp.opts.Datarow) - 1; col < sheet.MaxCol; col++ {
 			curr := importer.NewRowCells(col, prev)
 			for row := 0; row < sheet.MaxRow; row++ {
-				nameCol := int(wsOpts.Namerow) - 1
+				nameCol := int(sp.opts.Namerow) - 1
 				nameCell, err := sheet.Cell(row, nameCol)
 				if err != nil {
 					return errors.WithMessagef(err, "failed to get name cell: %d, %d", row, nameCol)
 				}
-				name := importer.ExtractFromCell(nameCell, wsOpts.Nameline)
+				name := importer.ExtractFromCell(nameCell, sp.opts.Nameline)
 
 				typ := ""
-				if wsOpts.Typerow > 0 {
+				if sp.opts.Typerow > 0 {
 					// if typerow is set!
-					typeCol := int(wsOpts.Typerow) - 1
+					typeCol := int(sp.opts.Typerow) - 1
 					typeCell, err := sheet.Cell(row, typeCol)
 					if err != nil {
 						return errors.WithMessagef(err, "failed to get name cell: %d, %d", row, typeCol)
 					}
-					typ = importer.ExtractFromCell(typeCell, wsOpts.Typeline)
+					typ = importer.ExtractFromCell(typeCell, sp.opts.Typeline)
 				}
 
 				data, err := sheet.Cell(row, col)
@@ -108,25 +110,25 @@ func (sp *sheetParser) Parse(protomsg proto.Message, sheet *importer.Sheet, wsOp
 		// namerow: name row
 		// [datarow, MaxRow]: data row
 		var prev *importer.RowCells
-		for row := int(wsOpts.Datarow) - 1; row < sheet.MaxRow; row++ {
+		for row := int(sp.opts.Datarow) - 1; row < sheet.MaxRow; row++ {
 			curr := importer.NewRowCells(row, prev)
 			for col := 0; col < sheet.MaxCol; col++ {
-				nameRow := int(wsOpts.Namerow) - 1
+				nameRow := int(sp.opts.Namerow) - 1
 				nameCell, err := sheet.Cell(nameRow, col)
 				if err != nil {
 					return errors.WithMessagef(err, "failed to get name cell: %d, %d", nameRow, col)
 				}
-				name := importer.ExtractFromCell(nameCell, wsOpts.Nameline)
+				name := importer.ExtractFromCell(nameCell, sp.opts.Nameline)
 
 				typ := ""
-				if wsOpts.Typerow > 0 {
+				if sp.opts.Typerow > 0 {
 					// if typerow is set!
-					typeRow := int(wsOpts.Typerow) - 1
+					typeRow := int(sp.opts.Typerow) - 1
 					typeCell, err := sheet.Cell(typeRow, col)
 					if err != nil {
 						return errors.WithMessagef(err, "failed to get type cell: %d, %d", typeRow, col)
 					}
-					typ = importer.ExtractFromCell(typeCell, wsOpts.Typeline)
+					typ = importer.ExtractFromCell(typeCell, sp.opts.Typeline)
 				}
 
 				data, err := sheet.Cell(row, col)
@@ -174,8 +176,8 @@ func (sp *sheetParser) parseFieldOptions(msg protoreflect.Message, rc *importer.
 		etype := tableaupb.Type_TYPE_DEFAULT
 		key := ""
 		layout := tableaupb.Layout_LAYOUT_DEFAULT
-		sep := ""
-		subsep := ""
+		sep := ","
+		subsep := ":"
 		optional := false
 
 		opts := fd.Options().(*descriptorpb.FieldOptions)
@@ -186,8 +188,8 @@ func (sp *sheetParser) parseFieldOptions(msg protoreflect.Message, rc *importer.
 			etype = fieldOpts.Type
 			key = fieldOpts.Key
 			layout = fieldOpts.Layout
-			sep = fieldOpts.Sep
-			subsep = fieldOpts.Subsep
+			sep = strings.TrimSpace(fieldOpts.Sep)
+			subsep = strings.TrimSpace(fieldOpts.Subsep)
 			optional = fieldOpts.Optional
 		} else {
 			// default processing
@@ -202,33 +204,17 @@ func (sp *sheetParser) parseFieldOptions(msg protoreflect.Message, rc *importer.
 			}
 		}
 		if sep == "" {
-			sep = ","
+			sep = strings.TrimSpace(sp.opts.Sep)
+			if sep == "" {
+				sep = ","
+			}
 		}
 		if subsep == "" {
-			subsep = ":"
-		}
-
-		// msgName := ""
-		// if fd.Kind() == protoreflect.MessageKind {
-		// 	msgName = string(fd.Message().FullName())
-		// }
-		// atom.Log.Debugf("%s%s(%v) %s(%s) %s = %d [(name) = \"%s\", (type) = %s, (key) = \"%s\", (layout) = \"%s\", (sep) = \"%s\"];",
-		// 	printer.Indent(depth), fd.Cardinality().String(), fd.IsMap(), fd.Kind().String(), msgName, fd.FullName().Name(), fd.Number(), prefix+name, etype.String(), key, layout.String(), sep)
-		// atom.Log.Debugw("field metadata",
-		// 	"tabs", depth,
-		// 	"cardinality", fd.Cardinality().String(),
-		// 	"isMap", fd.IsMap(),
-		// 	"kind", fd.Kind().String(),
-		// 	"msgName", msgName,
-		// 	"fullName", fd.FullName(),
-		// 	"number", fd.Number(),
-		// 	"name", prefix+name,
-		// 	"note", note,
-		// 	"type", etype.String(),
-		// 	"key", key,
-		// 	"layout", layout.String(),
-		// 	"sep", sep,
-		// )
+			subsep = strings.TrimSpace(sp.opts.Subsep)
+			if subsep == "" {
+				subsep = ":"
+			}
+		}
 
 		field := &Field{
 			fd: fd,
@@ -290,7 +276,7 @@ func (sp *sheetParser) parseMapField(field *Field, msg protoreflect.Message, rc
 			for _, pair := range splits {
 				kv := strings.Split(pair, field.opts.Subsep)
 				if len(kv) != 2 {
-					return errors.Errorf("%s|incell map: illegal key-value pair: %s", rc.CellDebugString(colName), pair)
+					return errors.Errorf("%s|incell map: illegal key-value pair: %s, subseq: '%s'", rc.CellDebugString(colName), pair, field.opts.Subsep)
 				}
 				key, value := kv[0], kv[1]
 
diff --git a/internal/importer/excel.go b/internal/importer/excel.go
index 8b51575..64a6af7 100644
--- a/internal/importer/excel.go
+++ b/internal/importer/excel.go
@@ -14,8 +14,8 @@ import (
 	"github.com/xuri/excelize/v2"
 )
 
-// metaSheetName defines the meta data of each worksheet.
-const metaSheetName = "@TABLEAU"
+// MetaSheetName defines the meta data of each worksheet.
+const MetaSheetName = "@TABLEAU"
 
 type ExcelImporter struct {
 	filename   string
@@ -80,7 +80,7 @@ func (x *ExcelImporter) parse() error {
 	}
 
 	if err := x.parseWorkbookMeta(file); err != nil {
-		return errors.Wrapf(err, "failed to parse workbook meta: %s", metaSheetName)
+		return errors.Wrapf(err, "failed to parse workbook meta: %s", MetaSheetName)
 	}
 
 	if err := x.collectSheetsInOrder(file); err != nil {
@@ -106,14 +106,14 @@ func (x *ExcelImporter) parseWorkbookMeta(file *excelize.File) error {
 		return nil
 	}
 
-	if file.GetSheetIndex(metaSheetName) == -1 {
-		atom.Log.Debugf("workbook %s has no sheet named %s", x.filename, metaSheetName)
+	if file.GetSheetIndex(MetaSheetName) == -1 {
+		atom.Log.Debugf("workbook %s has no sheet named %s", x.filename, MetaSheetName)
 		return nil
 	}
 
-	sheet, err := x.parseSheet(file, metaSheetName)
+	sheet, err := x.parseSheet(file, MetaSheetName)
 	if err != nil {
-		return errors.WithMessagef(err, "failed to parse sheet: %s#%s", x.filename, metaSheetName)
+		return errors.WithMessagef(err, "failed to parse sheet: %s#%s", x.filename, MetaSheetName)
 	}
 
 	if sheet.MaxRow <= 1 {
@@ -123,17 +123,12 @@ func (x *ExcelImporter) parseWorkbookMeta(file *excelize.File) error {
 			}
 		}
 		return nil
-	}
-	wsOpts := &tableaupb.WorksheetOptions{
-		Name:    sheet.Name,
-		Namerow: 1,
-		Datarow: 2,
-	}
-	if err := x.metaParser.Parse(x.Meta, sheet, wsOpts); err != nil {
-		return errors.WithMessagef(err, "failed to parse sheet: %s#%s", x.filename, metaSheetName)
+	}	
+	if err := x.metaParser.Parse(x.Meta, sheet); err != nil {
+		return errors.WithMessagef(err, "failed to parse sheet: %s#%s", x.filename, MetaSheetName)
 	}
 
-	atom.Log.Debugf("%s#%s: %+v", x.filename, metaSheetName, x.Meta)
+	atom.Log.Debugf("%s#%s: %+v", x.filename, MetaSheetName, x.Meta)
 	return nil
 }
 
@@ -182,7 +177,7 @@ func (x *ExcelImporter) collectSheetsInOrder(file *excelize.File) error {
 	x.sheetNames = nil
 	for _, val := range sortedMap.Values() {
 		sheetName := val.(string)
-		if  sheetName != metaSheetName || (x.includeMetaSheet && sheetName == metaSheetName) {
+		if  sheetName != MetaSheetName || (x.includeMetaSheet && sheetName == MetaSheetName) {
 			// exclude meta sheet
 			x.sheetNames = append(x.sheetNames, sheetName)
 		}
diff --git a/internal/importer/importer.go b/internal/importer/importer.go
index 828df00..0c883b9 100644
--- a/internal/importer/importer.go
+++ b/internal/importer/importer.go
@@ -33,7 +33,7 @@ func New(filename string, setters ...Option) Importer {
 	case format.CSV:
 		return NewCSVImporter(filename)
 	case format.XML:
-		return NewXMLImporter(filename, opts.Header)
+		return NewXMLImporter(filename, opts.Sheets, opts.Header)
 	default:
 		return nil
 	}
@@ -69,6 +69,27 @@ func NewSheet(name string, rows [][]string) *Sheet {
 	}
 }
 
+// ExtendSheet extends an existing Sheet.
+func ExtendSheet(sheet *Sheet, rows [][]string) {
+	maxRow := len(rows)
+	maxCol := 0
+	// MOTE: different row may have different length.
+	// We need to find the max col.
+	for _, row := range rows {
+		n := len(row)
+		if n > maxCol {
+			maxCol = n
+		}
+	}
+	sheet = &Sheet{
+		Name:   sheet.Name,
+		MaxRow: sheet.MaxRow + maxRow,
+		MaxCol: int(math.Max(float64(sheet.MaxCol), float64(maxCol))),
+		Rows:   append(sheet.Rows, rows...),
+		Meta:   sheet.Meta,
+	}
+}
+
 // Cell returns the cell at (row, col).
 func (s *Sheet) Cell(row, col int) (string, error) {
 	if row < 0 || row >= s.MaxRow {
diff --git a/internal/importer/options.go b/internal/importer/options.go
index 3426c81..0cc2e7a 100644
--- a/internal/importer/options.go
+++ b/internal/importer/options.go
@@ -3,12 +3,11 @@ package importer
 import (
 	"github.com/tableauio/tableau/format"
 	"github.com/tableauio/tableau/options"
-	"github.com/tableauio/tableau/proto/tableaupb"
 	"google.golang.org/protobuf/proto"
 )
 
 type SheetParser interface {
-	Parse(protomsg proto.Message, sheet *Sheet, wsOpts *tableaupb.WorksheetOptions) error
+	Parse(protomsg proto.Message, sheet *Sheet) error
 }
 
 type Options struct {
diff --git a/internal/importer/xml.go b/internal/importer/xml.go
index bbf33db..d7edb0c 100644
--- a/internal/importer/xml.go
+++ b/internal/importer/xml.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"encoding/xml"
 	"fmt"
+	"io"
 	"os"
 	"regexp"
 	"strconv"
@@ -20,18 +21,43 @@ import (
 	"github.com/tableauio/tableau/proto/tableaupb"
 )
 
+// metaName defines the meta data of each worksheet.
+const (
+	metaName         = "TABLEAU"
+	placeholderName  = "_placeholder"
+	placeholderType  = "bool"
+	placeholderValue = "false"
+)
+
+type Pass int
+
+const (
+	firstPass  Pass = 1
+	secondPass Pass = 2
+)
+
+type Range struct {
+	begin   int // index that Range begins at
+	attrNum int // the number of attrs with the same prefix
+	len     int // total number of columns with the same prefix, including attrs and children
+}
 type XMLImporter struct {
 	filename   string
-	sheetMap   map[string]*Sheet // sheet name -> sheet
+	sheetMap   map[string]*Sheet             // sheet name -> sheet
+	metaMap    map[string]*xlsxgen.MetaSheet // sheet name -> meta
 	sheetNames []string
 	header     *options.HeaderOption // header settings.
+
+	prefixMaps map[string](map[string]Range) // sheet -> { prefix -> [6, 9) }
 }
 
 // TODO: options
-func NewXMLImporter(filename string, header *options.HeaderOption) *XMLImporter {
+func NewXMLImporter(filename string, sheets []string, header *options.HeaderOption) *XMLImporter {
 	return &XMLImporter{
-		filename: filename,
-		header:   header,
+		filename:   filename,
+		sheetNames: sheets,
+		header:     header,
+		prefixMaps: make(map[string](map[string]Range)),
 	}
 }
 
@@ -70,6 +96,7 @@ func (x *XMLImporter) GetSheet(name string) (*Sheet, error) {
 
 func (x *XMLImporter) parse() error {
 	x.sheetMap = make(map[string]*Sheet)
+	x.metaMap = make(map[string]*xlsxgen.MetaSheet)
 	// open xml file and parse the document
 	xmlPath := x.filename
 	atom.Log.Debugf("xml: %s", xmlPath)
@@ -78,78 +105,171 @@ func (x *XMLImporter) parse() error {
 		return errors.Wrapf(err, "failed to open %s", xmlPath)
 	}
 	// replacement for `<` and `>` not allowed in attribute values
+	// e.g. `<ServerConf key="map<uint32,ServerConf> Open="bool">...</ServerConf>"`
 	attrValRegexp := regexp.MustCompile(`"\S+"`)
-	nudeRegexp := regexp.MustCompile(`<([A-Za-z0-9]+)>`)
-	keywordRegexp := regexp.MustCompile(`([</]+)@([A-Z]+)`)
 	replacedStr := attrValRegexp.ReplaceAllStringFunc(string(buf), func(s string) string {
 		var buf bytes.Buffer
 		xml.EscapeText(&buf, []byte(s[1:len(s)-1]))
 		return fmt.Sprintf("\"%s\"", buf.String())
 	})
-	replacedStr = nudeRegexp.ReplaceAllString(replacedStr, `<$1 TableauPlaceholder="0">`)
+	// replacement for tableau keyword, which begins by `@`
+	// e.g. `<@TABLEAU>``
+	keywordRegexp := regexp.MustCompile(`([</]+)@([A-Z]+)`)
 	replacedStr = keywordRegexp.ReplaceAllString(replacedStr, `$1$2`)
-	s := strings.NewReader(replacedStr)
-	p, err := xmlquery.CreateStreamParser(s, "/")
+	// Note that one xml file only has one root
+	// So in order to have multiple roots, we need to use a stream parser
+	// The first pass
+	hasMetaTag := false
+	hasUserSheets := x.sheetNames != nil
+	hasTableauSheets := false
+	contains := func(sheets []string, sheet string) bool {
+		for _, s := range sheets {
+			if sheet == s {
+				return true
+			}
+		}
+		return false
+	}
+	p, err := xmlquery.CreateStreamParser(strings.NewReader(replacedStr), "/")
 	if err != nil {
-		return errors.Wrapf(err, "failed to create parser for string %s", s)
+		return errors.Wrapf(err, "failed to create stream parser from string %s", replacedStr)
 	}
-	n, err := p.Read()
+	for {
+		n, err := p.Read()
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			return errors.Wrapf(err, "failed to read from stream parser")
+		}
+		// parse `<@TABLEAU>...</@TABLEAU>`
+		if n.Data == metaName {
+			hasMetaTag = true
+			nav := xmlquery.CreateXPathNavigator(n)
+			for flag := nav.MoveToChild(); flag; flag = nav.MoveToNext() {
+				// commentNode, documentNode and other meaningless nodes should be filtered
+				if nav.NodeType() != xpath.ElementNode {
+					continue
+				}
+				if !hasUserSheets {
+					x.sheetNames = append(x.sheetNames, nav.LocalName())
+				}
+				hasTableauSheets = true
+				if err := x.parseSheet(nav.Current(), nav.LocalName(), firstPass); err != nil {
+					return errors.WithMessagef(err, "failed to parse `@%s` sheet: %s#%s", metaName, x.filename, nav.LocalName())
+				}
+			}
+		} else {
+			// parse sheets
+			if (!hasUserSheets && !hasTableauSheets) || contains(x.sheetNames, n.Data) {
+				if err := x.parseSheet(n, n.Data, firstPass); err != nil {
+					return errors.WithMessagef(err, "failed to parse sheet: %s#%s", x.filename, n.Data)
+				}
+				if !hasUserSheets && !hasTableauSheets {
+					x.sheetNames = append(x.sheetNames, n.Data)
+				}
+			}
+		}
+		// `<@TABLEAU>...</@TABLEAU>` must be the first sheet
+		if !hasMetaTag {
+			atom.Log.Debugf("`<@TABLEAU>...</@TABLEAU>` not exists or not the first sheet")
+			x.sheetNames = nil
+			return nil
+		}
+	}
+
+	// The second pass
+	p, err = xmlquery.CreateStreamParser(strings.NewReader(replacedStr), "/")
 	if err != nil {
-		return errors.Wrapf(err, "failed to read from string %s", s)
+		return errors.Wrapf(err, "failed to create stream parser from string %s", replacedStr)
 	}
-	root := xmlquery.CreateXPathNavigator(n)
-	x.sheetNames = append(x.sheetNames, root.LocalName())
-	// generate data sheet
-	metaSheet := xlsxgen.NewMetaSheet(root.LocalName(), x.header, false)
-	if err := x.parseNode(root, metaSheet, int(metaSheet.Datarow)-1); err != nil {
-		return errors.Wrapf(err, "parseNode for root node %s failed", root.LocalName())
+	for {
+		n, err := p.Read()
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			return errors.Wrapf(err, "failed to read from stream parser")
+		}
+		// parse sheets
+		if contains(x.sheetNames, n.Data) {
+			if err := x.parseSheet(n, n.Data, secondPass); err != nil {
+				return errors.WithMessagef(err, "failed to parse sheet: %s#%s", x.filename, n.Data)
+			}
+		}
+	}
+
+	return nil
+}
+
+func (x *XMLImporter) parseSheet(doc *xmlquery.Node, sheetName string, pass Pass) error {
+	// In order to combine column headers (the result of 1 pass) and data (the result of 2 pass),
+	// we need to cache the MetaSheet struct in `x`
+	metaSheet, exist := x.metaMap[sheetName]
+	if !exist {
+		metaSheet = xlsxgen.NewMetaSheet(sheetName, x.header, false)
+		x.metaMap[sheetName] = metaSheet
+		x.prefixMaps[sheetName] = make(map[string]Range)
 	}
-	var rows [][]string
-	for i := 0; i < len(metaSheet.Rows); i++ {
-		var row []string
-		for _, cell := range metaSheet.Rows[i].Cells {
-			row = append(row, cell.Data)
+	root := xmlquery.CreateXPathNavigator(doc)
+	isMeta := doc.Parent != nil && doc.Parent.Data == metaName
+	switch pass {
+	case firstPass:
+		// 1 pass: scan all columns and their types
+		if err := x.parseNodeType(root, metaSheet, isMeta); err != nil {
+			return errors.Wrapf(err, "failed to parseNodeType for root node %s", sheetName)
+		}
+	case secondPass:
+		// 2 pass: fill data to the corresponding columns
+		if err := x.parseNodeData(root, metaSheet, int(metaSheet.Datarow)-1); err != nil {
+			return errors.Wrapf(err, "failed to parseNodeData for root node %s", sheetName)
 		}
-		rows = append(rows, row)
 	}
-	sheet := NewSheet(root.LocalName(), rows)
-	sheet.Meta = &tableaupb.SheetMeta{
-		Sheet:    root.LocalName(),
-		Alias:    root.LocalName(),
-		Nameline: 1,
-		Typeline: 1,
-		Nested:   true,
+	// atom.Log.Info(metaSheet)
+	if pass == secondPass {
+		// unpack rows from the MetaSheet struct
+		var rows [][]string
+		for i := 0; i < len(metaSheet.Rows); i++ {
+			var row []string
+			for _, cell := range metaSheet.Rows[i].Cells {
+				row = append(row, cell.Data)
+			}
+			rows = append(rows, row)
+		}
+		// insert sheets into map for importer
+		sheet := NewSheet(sheetName, rows)
+		sheet.Meta = &tableaupb.SheetMeta{
+			Sheet:    sheetName,
+			Alias:    sheetName,
+			Nameline: 1,
+			Typeline: 1,
+			Nested:   true,
+		}
+		x.sheetMap[sheetName] = sheet
 	}
-	x.sheetMap[root.LocalName()] = sheet
 	return nil
 }
 
-// parseNode parse and convert an xml file to sheet format
-func (x *XMLImporter) parseNode(nav *xmlquery.NodeNavigator, metaSheet *xlsxgen.MetaSheet, cursor int) error {
+// parseNodeType parse and convert an xml file to sheet format
+func (x *XMLImporter) parseNodeType(nav *xmlquery.NodeNavigator, metaSheet *xlsxgen.MetaSheet, isMeta bool) error {
 	// preprocess
-	realParent, prefix, isMeta := nav.Current().Parent, "", false
-	// skip `TABLEAU` to find real parent
+	prefix := ""
+	var parentList []string
+	// construct prefix
 	for flag, navCopy := true, *nav; flag && navCopy.LocalName() != metaSheet.Worksheet; flag = navCopy.MoveToParent() {
-		if navCopy.LocalName() == "TABLEAU" {
-			isMeta = true
-		} else {
-			prefix = strcase.ToCamel(navCopy.LocalName()) + prefix
-		}
-		if navCopy.Current().Parent != nil && navCopy.Current().Parent.Data == "TABLEAU" {
-			realParent = navCopy.Current().Parent.Parent
-		}
+		prefix = strcase.ToCamel(navCopy.LocalName()) + prefix
+		parentList = append(parentList, navCopy.LocalName())
 	}
-	repeated := len(xmlquery.Find(realParent, nav.LocalName())) > 1
+	repeated := len(xmlquery.Find(nav.Current().Parent, nav.LocalName())) > 1
 
-	// clear to the bottom
-	if navCopy := *nav; !navCopy.MoveToChild() {
-		for tmpCusor := cursor; tmpCusor < len(metaSheet.Rows); tmpCusor++ {
-			metaSheet.ForEachCol(tmpCusor, func(name string, cell *xlsxgen.Cell) error {
-				if strings.HasPrefix(name, prefix) {
-					cell.Data = metaSheet.GetDefaultValue(name)
-				}
-				return nil
-			})
+	// add placeholder to nude node
+	if !isMeta && len(nav.Current().Attr) == 0 {
+		colName := prefix + placeholderName
+		x.tryAddCol(metaSheet, parentList, placeholderName)
+		if nav.LocalName() != metaSheet.Worksheet {
+			metaSheet.SetColType(colName, fmt.Sprintf("{%s}%s", strcase.ToCamel(nav.LocalName()), placeholderType))
+		} else {
+			metaSheet.SetColType(colName, placeholderType)
 		}
 	}
 
@@ -157,9 +277,11 @@ func (x *XMLImporter) parseNode(nav *xmlquery.NodeNavigator, metaSheet *xlsxgen.
 	for i, attr := range nav.Current().Attr {
 		attrName := attr.Name.Local
 		attrValue := attr.Value
+		_, prefixExist := x.prefixMaps[metaSheet.Worksheet][prefix]
+		x.tryAddCol(metaSheet, parentList, strcase.ToCamel(attrName))
+
 		t, d := inferType(attrValue)
 		colName := prefix + strcase.ToCamel(attrName)
-		lastColName := metaSheet.GetLastColName()
 		metaSheet.SetDefaultValue(colName, d)
 		if isMeta {
 			if index := strings.Index(attrValue, "|"); index > 0 {
@@ -168,11 +290,6 @@ func (x *XMLImporter) parseNode(nav *xmlquery.NodeNavigator, metaSheet *xlsxgen.
 			} else {
 				t = attrValue
 			}
-		} else {
-			// fill values to the bottom when backtrace to top line
-			for tmpCusor := cursor; tmpCusor < len(metaSheet.Rows); tmpCusor++ {
-				metaSheet.Cell(tmpCusor, colName).Data = attrValue
-			}
 		}
 
 		curType := metaSheet.GetColType(colName)
@@ -183,9 +300,10 @@ func (x *XMLImporter) parseNode(nav *xmlquery.NodeNavigator, metaSheet *xlsxgen.
 		needChangeType := isMeta || curType == "" || (len(matches) > 0 && repeated)
 		// 1. new struct(list), not subsequent
 		// 2. {Type}int32 -> [Type]int32 (when mistaken it as a struct at first but discover multiple elements later)
-		setKeyedType := !strings.HasPrefix(lastColName, prefix) || (len(matches) > 0 && repeated)
+		setKeyedType := (!prefixExist || (len(matches) > 0 && repeated)) && nav.LocalName() != metaSheet.Worksheet
 		if needChangeType {
 			if matches := types.MatchMap(t); len(matches) == 3 {
+				// case 1: map<uint32,Type>
 				if !types.IsScalarType(matches[1]) && len(types.MatchEnum(matches[1])) == 0 {
 					return errors.Errorf("%s is not scalar type in node %s attr %s type %s", matches[1], nav.LocalName(), attrName, t)
 				}
@@ -194,6 +312,7 @@ func (x *XMLImporter) parseNode(nav *xmlquery.NodeNavigator, metaSheet *xlsxgen.
 				}
 				metaSheet.SetColType(colName, t)
 			} else if matches := types.MatchKeyedList(t); len(matches) == 3 {
+				// case 2: [Type]<uint32>
 				if i != 0 {
 					return errors.Errorf("KeyedList attr %s in node %s must be the first attr", attrName, nav.LocalName())
 				}
@@ -205,6 +324,7 @@ func (x *XMLImporter) parseNode(nav *xmlquery.NodeNavigator, metaSheet *xlsxgen.
 				}
 				metaSheet.SetColType(colName, t)
 			} else if matches := types.MatchList(t); len(matches) == 3 {
+				// case 3: [Type]uint32
 				if i != 0 {
 					return errors.Errorf("KeyedList attr %s in node %s must be the first attr", attrName, nav.LocalName())
 				}
@@ -216,17 +336,76 @@ func (x *XMLImporter) parseNode(nav *xmlquery.NodeNavigator, metaSheet *xlsxgen.
 				}
 				metaSheet.SetColType(colName, t)
 			} else if i == 0 && setKeyedType {
+				// case 4: {Type}uint32
 				if repeated {
 					metaSheet.SetColType(colName, fmt.Sprintf("[%s]<%s>", strcase.ToCamel(nav.LocalName()), t))
 				} else {
 					metaSheet.SetColType(colName, fmt.Sprintf("{%s}%s", strcase.ToCamel(nav.LocalName()), t))
 				}
 			} else {
+				// default: built-in type
 				metaSheet.SetColType(colName, t)
 			}
 		}
 	}
 
+	// iterate over child nodes
+	navCopy := *nav
+	for flag := navCopy.MoveToChild(); flag; flag = navCopy.MoveToNext() {
+		// commentNode, documentNode and other meaningless nodes should be filtered
+		if navCopy.NodeType() != xpath.ElementNode {
+			continue
+		}
+		tagName := navCopy.LocalName()
+		if err := x.parseNodeType(&navCopy, metaSheet, isMeta); err != nil {
+			return errors.Wrapf(err, "failed to parseNodeType for the node %s", tagName)
+		}
+	}
+
+	return nil
+}
+
+// parseNodeData parse and convert an xml file to sheet format
+func (x *XMLImporter) parseNodeData(nav *xmlquery.NodeNavigator, metaSheet *xlsxgen.MetaSheet, cursor int) error {
+	// preprocess
+	prefix := ""
+	// construct prefix
+	for flag, navCopy := true, *nav; flag && navCopy.LocalName() != metaSheet.Worksheet; flag = navCopy.MoveToParent() {
+		prefix = strcase.ToCamel(navCopy.LocalName()) + prefix
+	}
+
+	// clear to the bottom
+	if navCopy := *nav; !navCopy.MoveToChild() {
+		for tmpCusor := cursor; tmpCusor < len(metaSheet.Rows); tmpCusor++ {
+			metaSheet.ForEachCol(tmpCusor, func(name string, cell *xlsxgen.Cell) error {
+				if strings.HasPrefix(name, prefix) {
+					cell.Data = metaSheet.GetDefaultValue(name)
+				}
+				return nil
+			})
+		}
+	}
+
+	// add placeholder to nude node
+	if len(nav.Current().Attr) == 0 {
+		colName := prefix + placeholderName
+		// fill values to the bottom when backtrace to top line
+		for tmpCusor := cursor; tmpCusor < len(metaSheet.Rows); tmpCusor++ {
+			metaSheet.Cell(tmpCusor, len(metaSheet.Rows[metaSheet.Namerow-1].Cells), colName).Data = placeholderValue
+		}
+	}
+
+	// iterate over attributes
+	for _, attr := range nav.Current().Attr {
+		attrName := attr.Name.Local
+		attrValue := attr.Value
+		colName := prefix + strcase.ToCamel(attrName)
+		// fill values to the bottom when backtrace to top line
+		for tmpCusor := cursor; tmpCusor < len(metaSheet.Rows); tmpCusor++ {
+			metaSheet.Cell(tmpCusor, len(metaSheet.Rows[metaSheet.Namerow-1].Cells), colName).Data = attrValue
+		}
+	}
+
 	// iterate over child nodes
 	nodeMap := make(map[string]int)
 	navCopy := *nav
@@ -237,19 +416,15 @@ func (x *XMLImporter) parseNode(nav *xmlquery.NodeNavigator, metaSheet *xlsxgen.
 		}
 		tagName := navCopy.LocalName()
 		if count, existed := nodeMap[tagName]; existed {
-			// `TABLEAU` can only be placed in the first child node
-			if xmlquery.FindOne(navCopy.Current(), "/TABLEAU") != nil {
-				return errors.Errorf("`TABLEAU` found in node %s (index:%d) which is not the first child", tagName, count+1)
-			}
 			// duplicate means a list, should expand vertically
 			row := metaSheet.NewRow()
-			if err := x.parseNode(&navCopy, metaSheet, row.Index); err != nil {
-				return errors.Wrapf(err, "parseNode for node %s (index:%d) failed", tagName, count+1)
+			if err := x.parseNodeData(&navCopy, metaSheet, row.Index); err != nil {
+				return errors.Wrapf(err, "parseNodeData for node %s (index:%d) failed", tagName, count+1)
 			}
 			nodeMap[tagName]++
 		} else {
-			if err := x.parseNode(&navCopy, metaSheet, cursor); err != nil {
-				return errors.Wrapf(err, "parseNode for the first node %s failed", tagName)
+			if err := x.parseNodeData(&navCopy, metaSheet, cursor); err != nil {
+				return errors.Wrapf(err, "parseNodeData for the first node %s failed", tagName)
 			}
 			nodeMap[tagName] = 1
 		}
@@ -258,6 +433,54 @@ func (x *XMLImporter) parseNode(nav *xmlquery.NodeNavigator, metaSheet *xlsxgen.
 	return nil
 }
 
+// tryAddCol add a new column named `name` to an appropriate location in metaSheet if not exists or do nothing otherwise
+func (x *XMLImporter) tryAddCol(metaSheet *xlsxgen.MetaSheet, parentList []string, name string) {
+	prefix := ""
+	var reversedList []string
+	parentMap := make(map[string]bool)
+	prefixMap := x.prefixMaps[metaSheet.Worksheet]
+	for i := len(parentList) - 1; i >= 0; i-- {
+		prefix += parentList[i]
+		parentMap[prefix] = true
+		if i > 0 {
+			reversedList = append(reversedList, prefix)
+		}
+	}
+	colName := prefix + name
+	if metaSheet.HasCol(colName) {
+		return
+	}
+	shift := func(r Range) {
+		for i := 0; i < len(reversedList); i++ {
+			if r, exist := prefixMap[reversedList[i]]; exist {
+				prefixMap[reversedList[i]] = Range{r.begin, r.attrNum, r.len + 1}
+			}
+		}
+		for k, v := range prefixMap {
+			if _, exist := parentMap[k]; !exist && v.begin > r.begin {
+				prefixMap[k] = Range{v.begin + 1, v.attrNum, v.len}
+			}
+		}
+	}
+	// insert prefixMap
+	if r, exist := prefixMap[prefix]; !exist {
+		index := len(metaSheet.Rows[metaSheet.Namerow-1].Cells)
+		if len(reversedList) > 0 {
+			parentPrefix := reversedList[len(reversedList)-1]
+			if r2, exist := prefixMap[parentPrefix]; exist {
+				index = r2.begin + r2.len
+			}
+		}
+		prefixMap[prefix] = Range{index, 1, 1}
+		shift(prefixMap[prefix])
+		metaSheet.Cell(int(metaSheet.Namerow)-1, prefixMap[prefix].begin, colName).Data = colName
+	} else {
+		prefixMap[prefix] = Range{r.begin, r.attrNum + 1, r.len + 1}
+		shift(prefixMap[prefix])
+		metaSheet.Cell(int(metaSheet.Namerow)-1, r.begin+r.attrNum, colName).Data = colName
+	}
+}
+
 func inferType(value string) (string, string) {
 	var t, d string
 	if _, err := strconv.Atoi(value); err == nil {
diff --git a/internal/protogen/protogen.go b/internal/protogen/protogen.go
index 8877aa1..fdd5d35 100644
--- a/internal/protogen/protogen.go
+++ b/internal/protogen/protogen.go
@@ -164,7 +164,12 @@ func (gen *Generator) convert(dir, filename string) error {
 		return errors.WithMessagef(err, "get relative path failed")
 	}
 	absPath := filepath.Join(dir, filename)
-	parser := confgen.NewSheetParser(TableauProtoPackage, gen.LocationName)
+	wsOpts :=  &tableaupb.WorksheetOptions{
+		Name:    importer.MetaSheetName,
+		Namerow: 1,
+		Datarow: 2,
+	}
+	parser := confgen.NewSheetParser(TableauProtoPackage, gen.LocationName, wsOpts)
 	imp := importer.New(absPath, importer.Parser(parser), importer.Format(gen.InputOpts.Format), importer.Header(gen.Header))
 	sheets, err := imp.GetSheets()
 	if err != nil {
@@ -191,16 +196,19 @@ func (gen *Generator) convert(dir, filename string) error {
 		}
 		ws := &tableaupb.Worksheet{
 			Options: &tableaupb.WorksheetOptions{
-				Name:      sheet.Name,
-				Namerow:   gen.Header.Namerow,
-				Typerow:   gen.Header.Typerow,
-				Noterow:   gen.Header.Noterow,
-				Datarow:   gen.Header.Datarow,
-				Transpose: false,
-				Tags:      "",
-				Nameline:  sheet.Meta.Nameline,
-				Typeline:  sheet.Meta.Typeline,
-				Nested:    sheet.Meta.Nested,
+				Name:       sheet.Name,
+				Namerow:    gen.Header.Namerow,
+				Typerow:    gen.Header.Typerow,
+				Noterow:    gen.Header.Noterow,
+				Datarow:    gen.Header.Datarow,
+				Transpose:  false,
+				Tags:       "",
+				Nameline:   sheet.Meta.Nameline,
+				Typeline:   sheet.Meta.Typeline,
+				Nested:     sheet.Meta.Nested,
+				Sep:        sheet.Meta.Sep,
+				Subsep:     sheet.Meta.Subsep,
+				OrderedMap: sheet.Meta.OrderedMap,
 			},
 			Fields: []*tableaupb.Field{},
 			Name:   sheetMsgName,
diff --git a/internal/types/types.go b/internal/types/types.go
index d1a4287..3caf0e8 100644
--- a/internal/types/types.go
+++ b/internal/types/types.go
@@ -11,7 +11,7 @@ var enumRegexp *regexp.Regexp
 var boringIntegerRegexp *regexp.Regexp
 
 func init() {
-	mapRegexp = regexp.MustCompile(`^map<(.+),(.+)>`)       // e.g.: map<uint32,Type>
+	mapRegexp = regexp.MustCompile(`^map<(.+),(.+)>`)       // e.g.: map<uint32,Type>|{range:"1,10" refer:"XXXConf.ID"}
 	listRegexp = regexp.MustCompile(`^\[(.*)\](.+)`)        // e.g.: [Type]uint32
 	keyedListRegexp = regexp.MustCompile(`^\[(.*)\]<(.+)>`) // e.g.: [Type]<uint32>
 	structRegexp = regexp.MustCompile(`^\{(.+)\}(.+)`)      // e.g.: {Type}uint32
diff --git a/internal/xlsxgen/xlsxgen.go b/internal/xlsxgen/xlsxgen.go
index 49e5c55..f589ac8 100644
--- a/internal/xlsxgen/xlsxgen.go
+++ b/internal/xlsxgen/xlsxgen.go
@@ -23,12 +23,11 @@ import (
 	"google.golang.org/protobuf/types/descriptorpb"
 )
 
-
 type Generator struct {
 	ProtoPackage string // protobuf package name.
-	InputDir         string // input dir of workbooks.
-	OutputDir        string // output dir of generated protoconf files.
-	Workbook string // Workbook name
+	InputDir     string // input dir of workbooks.
+	OutputDir    string // output dir of generated protoconf files.
+	Workbook     string // Workbook name
 }
 
 var specialMessageMap = map[string]int{
@@ -46,9 +45,11 @@ type Row struct {
 type MetaSheet struct {
 	Worksheet string // worksheet name
 	options.HeaderOption
-	Transpose bool   // interchange the rows and columns
-	Rows []Row
+	Transpose bool // interchange the rows and columns
+
+	Rows   []Row
 	colMap map[string]int // colName -> colNum
+
 	defaultMap map[string]string // colName -> default value
 }
 
@@ -58,27 +59,34 @@ func NewMetaSheet(worksheet string, header *options.HeaderOption, transpose bool
 		rows[i].Index = i
 	}
 	return &MetaSheet{
-		Worksheet: worksheet,
+		Worksheet:    worksheet,
 		HeaderOption: *header,
-		Transpose: transpose,
-		Rows: rows,
-		colMap: make(map[string]int),
-		defaultMap: make(map[string]string),
+		Transpose:    transpose,
+		Rows:         rows,
+		colMap:       make(map[string]int),
+		defaultMap:   make(map[string]string),
 	}
 }
 
 func (sheet *MetaSheet) NewRow() *Row {
 	row := Row{
-		Cells: make([]Cell, len(sheet.Rows[len(sheet.Rows) - 1].Cells)),
+		Cells: make([]Cell, len(sheet.Rows[len(sheet.Rows)-1].Cells)),
 		Index: len(sheet.Rows),
 	}
 	// Critical!!! copy common value from parent node
-	copy(row.Cells, sheet.Rows[len(sheet.Rows) - 1].Cells)
+	copy(row.Cells, sheet.Rows[len(sheet.Rows)-1].Cells)
 	sheet.Rows = append(sheet.Rows, row)
 	return &row
 }
 
-func (sheet *MetaSheet) Cell(row int, name string) *Cell {
+func (sheet *MetaSheet) HasCol(name string) bool {
+	_, existed := sheet.colMap[name]
+	return existed
+}
+
+// Cell get the cell named `name` in the row `row`
+// If not exists, insert empty type and note to the cell located in (row, col)
+func (sheet *MetaSheet) Cell(row int, col int, name string) *Cell {
 	if col, existed := sheet.colMap[name]; existed {
 		if len(sheet.Rows[row].Cells) <= col {
 			newCols := make([]Cell, col-len(sheet.Rows[row].Cells)+1)
@@ -86,31 +94,60 @@ func (sheet *MetaSheet) Cell(row int, name string) *Cell {
 		}
 		return &sheet.Rows[row].Cells[col]
 	}
-	sheet.colMap[name] = len(sheet.Rows[sheet.Namerow-1].Cells)
-	sheet.Rows[sheet.Namerow-1].Cells = append(sheet.Rows[sheet.Namerow-1].Cells, Cell{Data: name})
+	// cannot access any of datarows when header not set
+	if row+1 >= int(sheet.Datarow) {
+		errStr := fmt.Sprintf("undefined column %s in row %d", name, row)
+		panic(errStr)
+	}
+	// cannot insert new column to an isolated location
+	curLen := len(sheet.Rows[sheet.Namerow-1].Cells)
+	if col > curLen || col < 0 {
+		errStr := fmt.Sprintf("invalid col %d, which should be in range [0,%d]", col, curLen)
+		panic(errStr)
+	}
+	// only define new column and its type
+	sheet.Rows[sheet.Namerow-1].Cells = append(sheet.Rows[sheet.Namerow-1].Cells, Cell{})
 	// if there is typerow and noterow
 	if sheet.Typerow < sheet.Datarow {
-		sheet.Rows[sheet.Typerow-1].Cells = append(sheet.Rows[sheet.Typerow-1].Cells, Cell{Data: ""})
+		sheet.Rows[sheet.Typerow-1].Cells = append(sheet.Rows[sheet.Typerow-1].Cells, Cell{})
 	}
 	if sheet.Noterow < sheet.Datarow {
-		sheet.Rows[sheet.Noterow-1].Cells = append(sheet.Rows[sheet.Noterow-1].Cells, Cell{Data: "Note"})
+		sheet.Rows[sheet.Noterow-1].Cells = append(sheet.Rows[sheet.Noterow-1].Cells, Cell{})
 	}
-	if row + 1 >= int(sheet.Datarow) {
-		sheet.Rows[row].Cells = append(sheet.Rows[row].Cells, Cell{Data: ""})
+	for i := curLen; i > col; i-- {
+		colName := sheet.Rows[sheet.Namerow-1].Cells[i-1].Data
+		sheet.colMap[colName] = i
+		sheet.Rows[sheet.Namerow-1].Cells[i] = sheet.Rows[sheet.Namerow-1].Cells[i-1]
+		// if there is typerow and noterow
+		if sheet.Typerow < sheet.Datarow {
+			sheet.Rows[sheet.Typerow-1].Cells[i] = sheet.Rows[sheet.Typerow-1].Cells[i-1]
+		}
+		if sheet.Noterow < sheet.Datarow {
+			sheet.Rows[sheet.Noterow-1].Cells[i] = sheet.Rows[sheet.Noterow-1].Cells[i-1]
+		}
+	}
+	sheet.colMap[name] = col
+	sheet.Rows[sheet.Namerow-1].Cells[col].Data = name
+	// if there is typerow and noterow
+	if sheet.Typerow < sheet.Datarow {
+		sheet.Rows[sheet.Typerow-1].Cells[col].Data = ""
+	}
+	if sheet.Noterow < sheet.Datarow {
+		sheet.Rows[sheet.Noterow-1].Cells[col].Data = "Note"
 	}
-	return &sheet.Rows[row].Cells[len(sheet.Rows[row].Cells)-1]
+	return &sheet.Rows[row].Cells[sheet.colMap[name]]
 }
 
 func (sheet *MetaSheet) SetColType(col, typ string) {
-	sheet.Cell(int(sheet.Typerow-1), col).Data = typ
+	sheet.Cell(int(sheet.Typerow-1), len(sheet.Rows[sheet.Namerow-1].Cells), col).Data = typ
 }
 
 func (sheet *MetaSheet) GetColType(col string) string {
-	return sheet.Cell(int(sheet.Typerow-1), col).Data
+	return sheet.Cell(int(sheet.Typerow-1), len(sheet.Rows[sheet.Namerow-1].Cells), col).Data
 }
 
 func (sheet *MetaSheet) SetColNote(col, note string) {
-	sheet.Cell(int(sheet.Noterow-1), col).Data = note
+	sheet.Cell(int(sheet.Noterow-1), len(sheet.Rows[sheet.Namerow-1].Cells), col).Data = note
 }
 
 func (sheet *MetaSheet) SetDefaultValue(col, defaultVal string) {
@@ -129,16 +166,16 @@ func (sheet *MetaSheet) GetDefaultValue(col string) string {
 }
 
 func (sheet *MetaSheet) GetLastColName() string {
-	row := sheet.Rows[sheet.Namerow - 1].Cells
+	row := sheet.Rows[sheet.Namerow-1].Cells
 	if len(row) == 0 {
 		return ""
 	}
-	return row[len(row) - 1].Data
+	return row[len(row)-1].Data
 }
 
 func (sheet *MetaSheet) ForEachCol(rowId int, f func(name string, cell *Cell) error) error {
 	for name, i := range sheet.colMap {
-		cell := sheet.Cell(rowId, name)
+		cell := sheet.Cell(rowId, len(sheet.Rows[sheet.Namerow-1].Cells), name)
 		if err := f(name, cell); err != nil {
 			return errors.Wrapf(err, "call user-defined failed when iterating col %s (%d, %d)", name, rowId, i)
 		}
diff --git a/load/load.go b/load/load.go
index 4cb7ecc..cced1a7 100644
--- a/load/load.go
+++ b/load/load.go
@@ -104,8 +104,8 @@ func loadExcel(msg proto.Message, dir string, format format.Format) error {
 	pkgName := md.ParentFile().Package()
 	// TODO: support LocationName setting by using Functional Options
 	locationName := ""
-	parser := confgen.NewSheetParser(string(pkgName), locationName)
-	if err := parser.Parse(msg, sheet, wsOpts); err != nil {
+	parser := confgen.NewSheetParser(string(pkgName), locationName, wsOpts)
+	if err := parser.Parse(msg, sheet); err != nil {
 		return errors.WithMessagef(err, "%v|failed to parse sheet: %s", msgName, wsOpts.Name)
 	}
 	return nil
diff --git a/proto/tableau/protobuf/meta.proto b/proto/tableau/protobuf/meta.proto
index 87233a7..36f3aa3 100644
--- a/proto/tableau/protobuf/meta.proto
+++ b/proto/tableau/protobuf/meta.proto
@@ -19,6 +19,8 @@ message SheetMeta {
   int32 nameline = 3 [(tableau.field) = { name: "Nameline" optional: true }];
   int32 typeline = 4 [(tableau.field) = { name: "Typeline" optional: true }];
   bool transpose = 5 [(tableau.field) = { name: "Transpose" optional: true }];
-  // nested naming of namerow
-  bool nested = 6 [(tableau.field) = { name: "Nested" optional: true }];
+  bool nested = 6 [(tableau.field) = { name: "Nested" optional: true }]; // nested naming of namerow
+  string sep = 7 [(tableau.field) = { name: "Sep" optional: true }];
+  string subsep = 8 [(tableau.field) = { name: "Subsep" optional: true }];
+  bool ordered_map = 9 [(tableau.field) = { name: "OrderedMap" optional: true }]; // generate ordered map accessers
 }
\ No newline at end of file
diff --git a/proto/tableau/protobuf/tableau.proto b/proto/tableau/protobuf/tableau.proto
index 411113a..44697f3 100644
--- a/proto/tableau/protobuf/tableau.proto
+++ b/proto/tableau/protobuf/tableau.proto
@@ -32,17 +32,20 @@ message WorkbookOptions {
 }
 
 message WorksheetOptions {
-  string name = 1;     // worksheet name
-  int32 namerow = 2;   // [default = 1]; // exact row number of name at worksheet.
-  int32 typerow = 3;   // [default = 2]; // exact row number of type at worksheet, for generating protos.
-  int32 noterow = 4;   // [default = 3]; // exact row number of note at worksheet.
-  int32 datarow = 5;   // [default = 4]; // start row number of data at worksheet.
-  bool transpose = 6;  // [default = false]; // interchange the rows and columns
-  string tags = 7;     // [default = ""]; // tags for usage, e.g.: "1,2" specifying loading servers. Speciallly, "*"
-                       // means all servers.
-  int32 nameline = 8;  // [default = 0]; // specify which line in cell as name, '0' means the whole cell is name.
-  int32 typeline = 9;  // [default = 0]; // specify which line in cell as type,'0' means the whole cell is type.
-  bool nested = 10;    // [default = false]; // whether the naming of name row is nested.
+  string name = 1;       // worksheet name
+  int32 namerow = 2;     // [default = 1]; // exact row number of name at worksheet.
+  int32 typerow = 3;     // [default = 2]; // exact row number of type at worksheet, for generating protos.
+  int32 noterow = 4;     // [default = 3]; // exact row number of note at worksheet.
+  int32 datarow = 5;     // [default = 4]; // start row number of data at worksheet.
+  bool transpose = 6;    // [default = false]; // interchange the rows and columns
+  string tags = 7;       // [default = ""]; // tags for usage, e.g.: "1,2" specifying loading servers. Speciallly, "*"
+                         // means all servers.
+  int32 nameline = 8;    // [default = 0]; // specify which line in cell as name, '0' means the whole cell is name.
+  int32 typeline = 9;    // [default = 0]; // specify which line in cell as type,'0' means the whole cell is type.
+  bool nested = 10;      // [default = false]; // whether the naming of name row is nested.
+  string sep = 11;       // separator, default: ","
+  string subsep = 12;    // subseparator, default: ":"
+  bool ordered_map = 13; // [default = false]; // whether generate OrderedMap accessers.
 }
 
 message FieldOptions {
@@ -52,19 +55,26 @@ message FieldOptions {
   string key = 4;     // only set when type is map
   Layout layout = 5;  // default: LAYOUT_DEFAULT
   string sep = 6;     // separator, default: ","
-  string subsep = 7;  // sub separator, default: ":"
+  string subsep = 7;  // subseparator, default: ":"
   bool optional = 8;  // whether the field is optional.
 
-  /////////////////////////////
-  // Simple Validators Below //
-  /////////////////////////////
-  // Different meanings:
-  // repeated: size range of array
-  // integer: value range
-  // string: count of utf-8 code point
-  int32 min = 11;     // min value
-  int32 max = 12;     // max value
-  string range = 13;  // format like set description: [1,10], (1,10], [1,10), [1,~]
+  FieldExtension extension = 15;
+}
+
+message FieldExtension {
+  // Usage: `{range:"1,10" refer:"XXXConf.ID"}`
+
+  // NOTE: two perspectives of the check:
+  //   - Local perspective, the check only depends on self configuration.
+  //   - Global perspective, the check depend on all configurations.
+
+  // Different interpretations of range::
+  //   - number: value range
+  //   - repeated: size range of array
+  //   - string: count of utf-8 code point
+  string range = 1;  // Local perspective: format: "1,10", "1,~", "~,10"
+  bool unique = 2;   // Local perspective: whether the field is unique when configured.
+  string refer = 3;  // Global perspective: ensure this field is in another message's field range. Example: "XXXConf.ID"
 }
 
 message EnumOptions {
diff --git a/proto/tableaupb/meta.pb.go b/proto/tableaupb/meta.pb.go
index 2b8a82a..f608926 100644
--- a/proto/tableaupb/meta.pb.go
+++ b/proto/tableaupb/meta.pb.go
@@ -72,13 +72,15 @@ type SheetMeta struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Sheet     string `protobuf:"bytes,1,opt,name=sheet,proto3" json:"sheet,omitempty"`
-	Alias     string `protobuf:"bytes,2,opt,name=alias,proto3" json:"alias,omitempty"`
-	Nameline  int32  `protobuf:"varint,3,opt,name=nameline,proto3" json:"nameline,omitempty"`
-	Typeline  int32  `protobuf:"varint,4,opt,name=typeline,proto3" json:"typeline,omitempty"`
-	Transpose bool   `protobuf:"varint,5,opt,name=transpose,proto3" json:"transpose,omitempty"`
-	// nested naming of namerow
-	Nested bool `protobuf:"varint,6,opt,name=nested,proto3" json:"nested,omitempty"`
+	Sheet      string `protobuf:"bytes,1,opt,name=sheet,proto3" json:"sheet,omitempty"`
+	Alias      string `protobuf:"bytes,2,opt,name=alias,proto3" json:"alias,omitempty"`
+	Nameline   int32  `protobuf:"varint,3,opt,name=nameline,proto3" json:"nameline,omitempty"`
+	Typeline   int32  `protobuf:"varint,4,opt,name=typeline,proto3" json:"typeline,omitempty"`
+	Transpose  bool   `protobuf:"varint,5,opt,name=transpose,proto3" json:"transpose,omitempty"`
+	Nested     bool   `protobuf:"varint,6,opt,name=nested,proto3" json:"nested,omitempty"` // nested naming of namerow
+	Sep        string `protobuf:"bytes,7,opt,name=sep,proto3" json:"sep,omitempty"`
+	Subsep     string `protobuf:"bytes,8,opt,name=subsep,proto3" json:"subsep,omitempty"`
+	OrderedMap bool   `protobuf:"varint,9,opt,name=ordered_map,json=orderedMap,proto3" json:"ordered_map,omitempty"` // generate ordered map accessers
 }
 
 func (x *SheetMeta) Reset() {
@@ -155,6 +157,27 @@ func (x *SheetMeta) GetNested() bool {
 	return false
 }
 
+func (x *SheetMeta) GetSep() string {
+	if x != nil {
+		return x.Sep
+	}
+	return ""
+}
+
+func (x *SheetMeta) GetSubsep() string {
+	if x != nil {
+		return x.Subsep
+	}
+	return ""
+}
+
+func (x *SheetMeta) GetOrderedMap() bool {
+	if x != nil {
+		return x.OrderedMap
+	}
+	return false
+}
+
 var File_meta_proto protoreflect.FileDescriptor
 
 var file_meta_proto_rawDesc = []byte{
@@ -173,7 +196,7 @@ var file_meta_proto_rawDesc = []byte{
 	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61,
 	0x75, 0x2e, 0x53, 0x68, 0x65, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c,
 	0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x12, 0x82, 0xb5, 0x18, 0x0e, 0x0a, 0x08, 0x40, 0x54,
-	0x41, 0x42, 0x4c, 0x45, 0x41, 0x55, 0x10, 0x01, 0x28, 0x02, 0x22, 0x88, 0x02, 0x0a, 0x09, 0x53,
+	0x41, 0x42, 0x4c, 0x45, 0x41, 0x55, 0x10, 0x01, 0x28, 0x02, 0x22, 0x84, 0x03, 0x0a, 0x09, 0x53,
 	0x68, 0x65, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x05, 0x73, 0x68, 0x65, 0x65,
 	0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0x82, 0xb5, 0x18, 0x07, 0x0a, 0x05, 0x53,
 	0x68, 0x65, 0x65, 0x74, 0x52, 0x05, 0x73, 0x68, 0x65, 0x65, 0x74, 0x12, 0x23, 0x0a, 0x05, 0x61,
@@ -190,10 +213,18 @@ var file_meta_proto_rawDesc = []byte{
 	0x40, 0x01, 0x52, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x26, 0x0a,
 	0x06, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x42, 0x0e, 0x82,
 	0xb5, 0x18, 0x0a, 0x0a, 0x06, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x40, 0x01, 0x52, 0x06, 0x6e,
-	0x65, 0x73, 0x74, 0x65, 0x64, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
-	0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x69, 0x6f, 0x2f, 0x74, 0x61,
-	0x62, 0x6c, 0x65, 0x61, 0x75, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c,
-	0x65, 0x61, 0x75, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x03, 0x73, 0x65, 0x70, 0x18, 0x07, 0x20, 0x01,
+	0x28, 0x09, 0x42, 0x0b, 0x82, 0xb5, 0x18, 0x07, 0x0a, 0x03, 0x53, 0x65, 0x70, 0x40, 0x01, 0x52,
+	0x03, 0x73, 0x65, 0x70, 0x12, 0x26, 0x0a, 0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70, 0x18, 0x08,
+	0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0x82, 0xb5, 0x18, 0x0a, 0x0a, 0x06, 0x53, 0x75, 0x62, 0x73,
+	0x65, 0x70, 0x40, 0x01, 0x52, 0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70, 0x12, 0x33, 0x0a, 0x0b,
+	0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28,
+	0x08, 0x42, 0x12, 0x82, 0xb5, 0x18, 0x0e, 0x0a, 0x0a, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64,
+	0x4d, 0x61, 0x70, 0x40, 0x01, 0x52, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x4d, 0x61,
+	0x70, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+	0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x69, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61,
+	0x75, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x70,
+	0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/proto/tableaupb/tableau.pb.go b/proto/tableaupb/tableau.pb.go
index 7483cf5..0081cf0 100644
--- a/proto/tableaupb/tableau.pb.go
+++ b/proto/tableaupb/tableau.pb.go
@@ -243,9 +243,12 @@ type WorksheetOptions struct {
 	Transpose bool   `protobuf:"varint,6,opt,name=transpose,proto3" json:"transpose,omitempty"` // [default = false]; // interchange the rows and columns
 	Tags      string `protobuf:"bytes,7,opt,name=tags,proto3" json:"tags,omitempty"`            // [default = ""]; // tags for usage, e.g.: "1,2" specifying loading servers. Speciallly, "*"
 	// means all servers.
-	Nameline int32 `protobuf:"varint,8,opt,name=nameline,proto3" json:"nameline,omitempty"` // [default = 0]; // specify which line in cell as name, '0' means the whole cell is name.
-	Typeline int32 `protobuf:"varint,9,opt,name=typeline,proto3" json:"typeline,omitempty"` // [default = 0]; // specify which line in cell as type,'0' means the whole cell is type.
-	Nested   bool  `protobuf:"varint,10,opt,name=nested,proto3" json:"nested,omitempty"`    // [default = false]; // whether the naming of name row is nested.
+	Nameline   int32  `protobuf:"varint,8,opt,name=nameline,proto3" json:"nameline,omitempty"`                        // [default = 0]; // specify which line in cell as name, '0' means the whole cell is name.
+	Typeline   int32  `protobuf:"varint,9,opt,name=typeline,proto3" json:"typeline,omitempty"`                        // [default = 0]; // specify which line in cell as type,'0' means the whole cell is type.
+	Nested     bool   `protobuf:"varint,10,opt,name=nested,proto3" json:"nested,omitempty"`                           // [default = false]; // whether the naming of name row is nested.
+	Sep        string `protobuf:"bytes,11,opt,name=sep,proto3" json:"sep,omitempty"`                                  // separator, default: ","
+	Subsep     string `protobuf:"bytes,12,opt,name=subsep,proto3" json:"subsep,omitempty"`                            // subseparator, default: ":"
+	OrderedMap bool   `protobuf:"varint,13,opt,name=ordered_map,json=orderedMap,proto3" json:"ordered_map,omitempty"` // [default = false]; // whether generate OrderedMap accessers.
 }
 
 func (x *WorksheetOptions) Reset() {
@@ -350,29 +353,41 @@ func (x *WorksheetOptions) GetNested() bool {
 	return false
 }
 
+func (x *WorksheetOptions) GetSep() string {
+	if x != nil {
+		return x.Sep
+	}
+	return ""
+}
+
+func (x *WorksheetOptions) GetSubsep() string {
+	if x != nil {
+		return x.Subsep
+	}
+	return ""
+}
+
+func (x *WorksheetOptions) GetOrderedMap() bool {
+	if x != nil {
+		return x.OrderedMap
+	}
+	return false
+}
+
 type FieldOptions struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Name     string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`                          // scalar type's varible name or composite type's varible name (prefix)
-	Note     string `protobuf:"bytes,2,opt,name=note,proto3" json:"note,omitempty"`                          // note of name, maybe in another language (Chinese) other than name (English)
-	Type     Type   `protobuf:"varint,3,opt,name=type,proto3,enum=tableau.Type" json:"type,omitempty"`       // default: TYPE_DEFAULT
-	Key      string `protobuf:"bytes,4,opt,name=key,proto3" json:"key,omitempty"`                            // only set when type is map
-	Layout   Layout `protobuf:"varint,5,opt,name=layout,proto3,enum=tableau.Layout" json:"layout,omitempty"` // default: LAYOUT_DEFAULT
-	Sep      string `protobuf:"bytes,6,opt,name=sep,proto3" json:"sep,omitempty"`                            // separator, default: ","
-	Subsep   string `protobuf:"bytes,7,opt,name=subsep,proto3" json:"subsep,omitempty"`                      // sub separator, default: ":"
-	Optional bool   `protobuf:"varint,8,opt,name=optional,proto3" json:"optional,omitempty"`                 // whether the field is optional.
-	/////////////////////////////
-	// Simple Validators Below //
-	/////////////////////////////
-	// Different meanings:
-	// repeated: size range of array
-	// integer: value range
-	// string: count of utf-8 code point
-	Min   int32  `protobuf:"varint,11,opt,name=min,proto3" json:"min,omitempty"`    // min value
-	Max   int32  `protobuf:"varint,12,opt,name=max,proto3" json:"max,omitempty"`    // max value
-	Range string `protobuf:"bytes,13,opt,name=range,proto3" json:"range,omitempty"` // format like set description: [1,10], (1,10], [1,10), [1,~]
+	Name      string          `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`                          // scalar type's varible name or composite type's varible name (prefix)
+	Note      string          `protobuf:"bytes,2,opt,name=note,proto3" json:"note,omitempty"`                          // note of name, maybe in another language (Chinese) other than name (English)
+	Type      Type            `protobuf:"varint,3,opt,name=type,proto3,enum=tableau.Type" json:"type,omitempty"`       // default: TYPE_DEFAULT
+	Key       string          `protobuf:"bytes,4,opt,name=key,proto3" json:"key,omitempty"`                            // only set when type is map
+	Layout    Layout          `protobuf:"varint,5,opt,name=layout,proto3,enum=tableau.Layout" json:"layout,omitempty"` // default: LAYOUT_DEFAULT
+	Sep       string          `protobuf:"bytes,6,opt,name=sep,proto3" json:"sep,omitempty"`                            // separator, default: ","
+	Subsep    string          `protobuf:"bytes,7,opt,name=subsep,proto3" json:"subsep,omitempty"`                      // subseparator, default: ":"
+	Optional  bool            `protobuf:"varint,8,opt,name=optional,proto3" json:"optional,omitempty"`                 // whether the field is optional.
+	Extension *FieldExtension `protobuf:"bytes,15,opt,name=extension,proto3" json:"extension,omitempty"`
 }
 
 func (x *FieldOptions) Reset() {
@@ -463,27 +478,80 @@ func (x *FieldOptions) GetOptional() bool {
 	return false
 }
 
-func (x *FieldOptions) GetMin() int32 {
+func (x *FieldOptions) GetExtension() *FieldExtension {
 	if x != nil {
-		return x.Min
+		return x.Extension
 	}
-	return 0
+	return nil
 }
 
-func (x *FieldOptions) GetMax() int32 {
-	if x != nil {
-		return x.Max
+type FieldExtension struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Different interpretations of range::
+	//   - number: value range
+	//   - repeated: size range of array
+	//   - string: count of utf-8 code point
+	Range  string `protobuf:"bytes,1,opt,name=range,proto3" json:"range,omitempty"`    // Local perspective: format: "1,10", "1,~", "~,10"
+	Unique bool   `protobuf:"varint,2,opt,name=unique,proto3" json:"unique,omitempty"` // Local perspective: whether the field is unique when configured.
+	Refer  string `protobuf:"bytes,3,opt,name=refer,proto3" json:"refer,omitempty"`    // Global perspective: ensure this field is in another message's field range. Example: "XXXConf.ID"
+}
+
+func (x *FieldExtension) Reset() {
+	*x = FieldExtension{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_tableau_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
 	}
-	return 0
 }
 
-func (x *FieldOptions) GetRange() string {
+func (x *FieldExtension) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FieldExtension) ProtoMessage() {}
+
+func (x *FieldExtension) ProtoReflect() protoreflect.Message {
+	mi := &file_tableau_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use FieldExtension.ProtoReflect.Descriptor instead.
+func (*FieldExtension) Descriptor() ([]byte, []int) {
+	return file_tableau_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *FieldExtension) GetRange() string {
 	if x != nil {
 		return x.Range
 	}
 	return ""
 }
 
+func (x *FieldExtension) GetUnique() bool {
+	if x != nil {
+		return x.Unique
+	}
+	return false
+}
+
+func (x *FieldExtension) GetRefer() string {
+	if x != nil {
+		return x.Refer
+	}
+	return ""
+}
+
 type EnumOptions struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -495,7 +563,7 @@ type EnumOptions struct {
 func (x *EnumOptions) Reset() {
 	*x = EnumOptions{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_tableau_proto_msgTypes[3]
+		mi := &file_tableau_proto_msgTypes[4]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -508,7 +576,7 @@ func (x *EnumOptions) String() string {
 func (*EnumOptions) ProtoMessage() {}
 
 func (x *EnumOptions) ProtoReflect() protoreflect.Message {
-	mi := &file_tableau_proto_msgTypes[3]
+	mi := &file_tableau_proto_msgTypes[4]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -521,7 +589,7 @@ func (x *EnumOptions) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use EnumOptions.ProtoReflect.Descriptor instead.
 func (*EnumOptions) Descriptor() ([]byte, []int) {
-	return file_tableau_proto_rawDescGZIP(), []int{3}
+	return file_tableau_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *EnumOptions) GetName() string {
@@ -542,7 +610,7 @@ type EnumValueOptions struct {
 func (x *EnumValueOptions) Reset() {
 	*x = EnumValueOptions{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_tableau_proto_msgTypes[4]
+		mi := &file_tableau_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -555,7 +623,7 @@ func (x *EnumValueOptions) String() string {
 func (*EnumValueOptions) ProtoMessage() {}
 
 func (x *EnumValueOptions) ProtoReflect() protoreflect.Message {
-	mi := &file_tableau_proto_msgTypes[4]
+	mi := &file_tableau_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -568,7 +636,7 @@ func (x *EnumValueOptions) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use EnumValueOptions.ProtoReflect.Descriptor instead.
 func (*EnumValueOptions) Descriptor() ([]byte, []int) {
-	return file_tableau_proto_rawDescGZIP(), []int{4}
+	return file_tableau_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *EnumValueOptions) GetName() string {
@@ -660,7 +728,7 @@ var file_tableau_proto_rawDesc = []byte{
 	0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x0f, 0x57, 0x6f,
 	0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a,
 	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
-	0x65, 0x22, 0x90, 0x02, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74, 0x4f,
+	0x65, 0x22, 0xdb, 0x02, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65, 0x74, 0x4f,
 	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
 	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x61,
 	0x6d, 0x65, 0x72, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6e, 0x61, 0x6d,
@@ -677,81 +745,91 @@ var file_tableau_proto_rawDesc = []byte{
 	0x12, 0x1a, 0x0a, 0x08, 0x74, 0x79, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x09, 0x20, 0x01,
 	0x28, 0x05, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06,
 	0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6e, 0x65,
-	0x73, 0x74, 0x65, 0x64, 0x22, 0x94, 0x02, 0x0a, 0x0c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70,
-	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74,
-	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x12, 0x21, 0x0a,
-	0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x74, 0x61,
-	0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
-	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
-	0x65, 0x79, 0x12, 0x27, 0x0a, 0x06, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01,
-	0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x4c, 0x61, 0x79,
-	0x6f, 0x75, 0x74, 0x52, 0x06, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73,
-	0x65, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x65, 0x70, 0x12, 0x16, 0x0a,
-	0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73,
-	0x75, 0x62, 0x73, 0x65, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61,
-	0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61,
-	0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03,
-	0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05,
-	0x52, 0x03, 0x6d, 0x61, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x0d,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x21, 0x0a, 0x0b, 0x45,
-	0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26,
-	0x0a, 0x10, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0xa0, 0x02, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
-	0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10,
-	0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10,
-	0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32,
-	0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x36, 0x34,
-	0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x36,
-	0x34, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x4f, 0x55, 0x42,
-	0x4c, 0x45, 0x10, 0x05, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x4f,
-	0x41, 0x54, 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4f,
-	0x4c, 0x10, 0x07, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x49,
-	0x4e, 0x47, 0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x52,
-	0x55, 0x43, 0x54, 0x10, 0x0a, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41,
-	0x54, 0x45, 0x10, 0x15, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x49, 0x4d,
-	0x45, 0x10, 0x16, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x45,
-	0x54, 0x49, 0x4d, 0x45, 0x10, 0x17, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49,
-	0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x18, 0x12, 0x13, 0x0a, 0x0f,
-	0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x5f, 0x4d, 0x41, 0x50, 0x10,
-	0x19, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x43, 0x45, 0x4c, 0x4c,
-	0x5f, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x10, 0x1a, 0x2a, 0x48, 0x0a, 0x06, 0x4c, 0x61, 0x79,
-	0x6f, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, 0x44, 0x45,
-	0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x41, 0x59, 0x4f, 0x55,
-	0x54, 0x5f, 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11,
-	0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, 0x48, 0x4f, 0x52, 0x49, 0x5a, 0x4f, 0x4e, 0x54, 0x41,
-	0x4c, 0x10, 0x02, 0x3a, 0x54, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x12,
-	0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
-	0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86,
-	0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e,
-	0x57, 0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52,
-	0x08, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x5a, 0x0a, 0x09, 0x77, 0x6f, 0x72,
-	0x6b, 0x73, 0x68, 0x65, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+	0x73, 0x74, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x03, 0x73, 0x65, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70,
+	0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70, 0x12, 0x1f,
+	0x0a, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x0d, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x22,
+	0x91, 0x02, 0x0a, 0x0c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75,
+	0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b,
+	0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a,
+	0x06, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e,
+	0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x06,
+	0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x70, 0x18, 0x06, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x65, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x75, 0x62, 0x73,
+	0x65, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x75, 0x62, 0x73, 0x65, 0x70,
+	0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01,
+	0x28, 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x35, 0x0a, 0x09,
+	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x17, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45,
+	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
+	0x69, 0x6f, 0x6e, 0x22, 0x54, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x78, 0x74, 0x65,
+	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75,
+	0x6e, 0x69, 0x71, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x6e, 0x69,
+	0x71, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x66, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x66, 0x65, 0x72, 0x22, 0x21, 0x0a, 0x0b, 0x45, 0x6e, 0x75,
+	0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x10,
+	0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x2a, 0xa0, 0x02, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a,
+	0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12,
+	0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x01, 0x12,
+	0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x02,
+	0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x03,
+	0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10,
+	0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x4f, 0x55, 0x42, 0x4c, 0x45,
+	0x10, 0x05, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x4f, 0x41, 0x54,
+	0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4f, 0x4c, 0x10,
+	0x07, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47,
+	0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x55, 0x43,
+	0x54, 0x10, 0x0a, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x45,
+	0x10, 0x15, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x10,
+	0x16, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x45, 0x54, 0x49,
+	0x4d, 0x45, 0x10, 0x17, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x43,
+	0x45, 0x4c, 0x4c, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x18, 0x12, 0x13, 0x0a, 0x0f, 0x54, 0x59,
+	0x50, 0x45, 0x5f, 0x49, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x5f, 0x4d, 0x41, 0x50, 0x10, 0x19, 0x12,
+	0x16, 0x0a, 0x12, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x5f, 0x53,
+	0x54, 0x52, 0x55, 0x43, 0x54, 0x10, 0x1a, 0x2a, 0x48, 0x0a, 0x06, 0x4c, 0x61, 0x79, 0x6f, 0x75,
+	0x74, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, 0x44, 0x45, 0x46, 0x41,
+	0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f,
+	0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x41,
+	0x59, 0x4f, 0x55, 0x54, 0x5f, 0x48, 0x4f, 0x52, 0x49, 0x5a, 0x4f, 0x4e, 0x54, 0x41, 0x4c, 0x10,
+	0x02, 0x3a, 0x54, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1c, 0x2e,
+	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+	0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x57, 0x6f,
+	0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x08, 0x77,
+	0x6f, 0x72, 0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x5a, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73,
+	0x68, 0x65, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e,
+	0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x68, 0x65, 0x65,
+	0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x68,
+	0x65, 0x65, 0x74, 0x3a, 0x4c, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1d, 0x2e, 0x67,
+	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46,
+	0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x46, 0x69,
+	0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c,
+	0x64, 0x3a, 0x48, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d,
 	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x19, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x68,
-	0x65, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b,
-	0x73, 0x68, 0x65, 0x65, 0x74, 0x3a, 0x4c, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1d,
-	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
-	0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86,
-	0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e,
-	0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x05, 0x66, 0x69,
-	0x65, 0x6c, 0x64, 0x3a, 0x48, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x1c, 0x2e, 0x67, 0x6f,
-	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e,
-	0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x14, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x45, 0x6e, 0x75, 0x6d,
-	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x3a, 0x56, 0x0a,
-	0x06, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
-	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61,
-	0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x45, 0x6e, 0x75,
-	0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x06, 0x65,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
-	0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x69, 0x6f, 0x2f, 0x74, 0x61,
-	0x62, 0x6c, 0x65, 0x61, 0x75, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c,
-	0x65, 0x61, 0x75, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x14, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x3a, 0x56, 0x0a, 0x06, 0x65,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75,
+	0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x19, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56,
+	0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x06, 0x65, 0x76, 0x61,
+	0x6c, 0x75, 0x65, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
+	0x6d, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61, 0x75, 0x69, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c,
+	0x65, 0x61, 0x75, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x61,
+	0x75, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -767,39 +845,41 @@ func file_tableau_proto_rawDescGZIP() []byte {
 }
 
 var file_tableau_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
-var file_tableau_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_tableau_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
 var file_tableau_proto_goTypes = []interface{}{
 	(Type)(0),                             // 0: tableau.Type
 	(Layout)(0),                           // 1: tableau.Layout
 	(*WorkbookOptions)(nil),               // 2: tableau.WorkbookOptions
 	(*WorksheetOptions)(nil),              // 3: tableau.WorksheetOptions
 	(*FieldOptions)(nil),                  // 4: tableau.FieldOptions
-	(*EnumOptions)(nil),                   // 5: tableau.EnumOptions
-	(*EnumValueOptions)(nil),              // 6: tableau.EnumValueOptions
-	(*descriptorpb.FileOptions)(nil),      // 7: google.protobuf.FileOptions
-	(*descriptorpb.MessageOptions)(nil),   // 8: google.protobuf.MessageOptions
-	(*descriptorpb.FieldOptions)(nil),     // 9: google.protobuf.FieldOptions
-	(*descriptorpb.EnumOptions)(nil),      // 10: google.protobuf.EnumOptions
-	(*descriptorpb.EnumValueOptions)(nil), // 11: google.protobuf.EnumValueOptions
+	(*FieldExtension)(nil),                // 5: tableau.FieldExtension
+	(*EnumOptions)(nil),                   // 6: tableau.EnumOptions
+	(*EnumValueOptions)(nil),              // 7: tableau.EnumValueOptions
+	(*descriptorpb.FileOptions)(nil),      // 8: google.protobuf.FileOptions
+	(*descriptorpb.MessageOptions)(nil),   // 9: google.protobuf.MessageOptions
+	(*descriptorpb.FieldOptions)(nil),     // 10: google.protobuf.FieldOptions
+	(*descriptorpb.EnumOptions)(nil),      // 11: google.protobuf.EnumOptions
+	(*descriptorpb.EnumValueOptions)(nil), // 12: google.protobuf.EnumValueOptions
 }
 var file_tableau_proto_depIdxs = []int32{
 	0,  // 0: tableau.FieldOptions.type:type_name -> tableau.Type
 	1,  // 1: tableau.FieldOptions.layout:type_name -> tableau.Layout
-	7,  // 2: tableau.workbook:extendee -> google.protobuf.FileOptions
-	8,  // 3: tableau.worksheet:extendee -> google.protobuf.MessageOptions
-	9,  // 4: tableau.field:extendee -> google.protobuf.FieldOptions
-	10, // 5: tableau.enum:extendee -> google.protobuf.EnumOptions
-	11, // 6: tableau.evalue:extendee -> google.protobuf.EnumValueOptions
-	2,  // 7: tableau.workbook:type_name -> tableau.WorkbookOptions
-	3,  // 8: tableau.worksheet:type_name -> tableau.WorksheetOptions
-	4,  // 9: tableau.field:type_name -> tableau.FieldOptions
-	5,  // 10: tableau.enum:type_name -> tableau.EnumOptions
-	6,  // 11: tableau.evalue:type_name -> tableau.EnumValueOptions
-	12, // [12:12] is the sub-list for method output_type
-	12, // [12:12] is the sub-list for method input_type
-	7,  // [7:12] is the sub-list for extension type_name
-	2,  // [2:7] is the sub-list for extension extendee
-	0,  // [0:2] is the sub-list for field type_name
+	5,  // 2: tableau.FieldOptions.extension:type_name -> tableau.FieldExtension
+	8,  // 3: tableau.workbook:extendee -> google.protobuf.FileOptions
+	9,  // 4: tableau.worksheet:extendee -> google.protobuf.MessageOptions
+	10, // 5: tableau.field:extendee -> google.protobuf.FieldOptions
+	11, // 6: tableau.enum:extendee -> google.protobuf.EnumOptions
+	12, // 7: tableau.evalue:extendee -> google.protobuf.EnumValueOptions
+	2,  // 8: tableau.workbook:type_name -> tableau.WorkbookOptions
+	3,  // 9: tableau.worksheet:type_name -> tableau.WorksheetOptions
+	4,  // 10: tableau.field:type_name -> tableau.FieldOptions
+	6,  // 11: tableau.enum:type_name -> tableau.EnumOptions
+	7,  // 12: tableau.evalue:type_name -> tableau.EnumValueOptions
+	13, // [13:13] is the sub-list for method output_type
+	13, // [13:13] is the sub-list for method input_type
+	8,  // [8:13] is the sub-list for extension type_name
+	3,  // [3:8] is the sub-list for extension extendee
+	0,  // [0:3] is the sub-list for field type_name
 }
 
 func init() { file_tableau_proto_init() }
@@ -845,7 +925,7 @@ func file_tableau_proto_init() {
 			}
 		}
 		file_tableau_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*EnumOptions); i {
+			switch v := v.(*FieldExtension); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -857,6 +937,18 @@ func file_tableau_proto_init() {
 			}
 		}
 		file_tableau_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*EnumOptions); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_tableau_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*EnumValueOptions); i {
 			case 0:
 				return &v.state
@@ -875,7 +967,7 @@ func file_tableau_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_tableau_proto_rawDesc,
 			NumEnums:      2,
-			NumMessages:   5,
+			NumMessages:   6,
 			NumExtensions: 5,
 			NumServices:   0,
 		},
diff --git a/tableau.go b/tableau.go
index 106858d..ddc9ab0 100644
--- a/tableau.go
+++ b/tableau.go
@@ -10,6 +10,7 @@ import (
 	"github.com/tableauio/tableau/internal/protogen"
 	"github.com/tableauio/tableau/internal/xlsxgen"
 	"github.com/tableauio/tableau/options"
+	"github.com/tableauio/tableau/proto/tableaupb"
 )
 
 // Excel2Conf converts excel files (with tableau header) to different formatted configuration files.
@@ -49,7 +50,12 @@ func Proto2Excel(protoPackage, indir, outdir string) {
 
 // ParseMeta parses the @TABLEAU sheet in a workbook.
 func ParseMeta(indir, relWorkbookPath string) importer.Importer {
-	parser := confgen.NewSheetParser(protogen.TableauProtoPackage, "")
+	wsOpts :=  &tableaupb.WorksheetOptions{
+		Name:    importer.MetaSheetName,
+		Namerow: 1,
+		Datarow: 2,
+	}
+	parser := confgen.NewSheetParser(protogen.TableauProtoPackage, "", wsOpts)
 	return importer.New(
 		filepath.Join(indir, relWorkbookPath),
 		importer.Parser(parser),


Test patch:
diff --git a/test/protoconf/excel/test_conf.proto b/test/protoconf/excel/test_conf.proto
index 40ce1cd..2f591ec 100644
--- a/test/protoconf/excel/test_conf.proto
+++ b/test/protoconf/excel/test_conf.proto
@@ -12,53 +12,53 @@ import "time.proto";
 option (tableau.workbook) = {name:"Test.xlsx"};
 
 message Activity {
-  option (tableau.worksheet) = {name:"Activity" namerow:1 typerow:2 noterow:3 datarow:5 nameline:1 typeline:1};
+  option (tableau.worksheet) = {name:"Activity" namerow:1 typerow:2 noterow:3 datarow:5 nameline:1 typeline:1 nested:true sep:"," ordered_map:true};
 
-  map<uint32, Activity> activity_map = 1 [(tableau.field) = {key:"ActivityID" layout:LAYOUT_VERTICAL}];
+  map<uint32, Activity> activity_map = 1 [(tableau.field) = {name:"Activity" key:"ID" layout:LAYOUT_VERTICAL}];
   message Activity {
-    uint32 activity_id = 1 [(tableau.field) = {name:"ActivityID"}];
-    string activity_name = 2 [(tableau.field) = {name:"ActivityName"}];
-    map<uint32, Chapter> chapter_map = 3 [(tableau.field) = {key:"ChapterID" layout:LAYOUT_VERTICAL}];
-    message Chapter {
-      uint32 chapter_id = 1 [(tableau.field) = {name:"ChapterID"}];
-      string chapter_name = 2 [(tableau.field) = {name:"ChapterName"}];
-      repeated Section section_list = 3 [(tableau.field) = {layout:LAYOUT_VERTICAL}];
-      message Section {
-        uint32 section_id = 1 [(tableau.field) = {name:"SectionID"}];
-        string section_name = 2 [(tableau.field) = {name:"SectionName"}];
-        repeated Item section_item_list = 3 [(tableau.field) = {name:"SectionItem" layout:LAYOUT_HORIZONTAL}];
-        message Item {
-          int32 id = 1 [(tableau.field) = {name:"Id"}];
-          int32 num = 2 [(tableau.field) = {name:"Num"}];
-        }
-        Duration duration = 4 [(tableau.field) = {name:"Duration"}];
-        google.protobuf.Duration timespan = 5 [(tableau.field) = {name:"Timespan"}];
-        Task task = 6 [(tableau.field) = {name:"Task"}];
-        message Task {
-          int32 type = 1 [(tableau.field) = {name:"Type"}];
-          int32 param_1 = 2 [(tableau.field) = {name:"Param1"}];
-          int32 param_2 = 3 [(tableau.field) = {name:"Param2"}];
-          int32 param_3 = 4 [(tableau.field) = {name:"Param3"}];
-          repeated int32 param_list_1 = 5 [(tableau.field) = {name:"ParamList1" type:TYPE_INCELL_LIST}];
-          repeated int32 param_list_2 = 6 [(tableau.field) = {name:"ParamList2" type:TYPE_INCELL_LIST}];
-          repeated int32 param_list_3 = 7 [(tableau.field) = {name:"ParamList3" type:TYPE_INCELL_LIST}];
-          map<int32, int32> param_map_1 = 8 [(tableau.field) = {name:"ParamMap1" type:TYPE_INCELL_MAP}];
-          map<int32, int32> param_map_2 = 9 [(tableau.field) = {name:"ParamMap2" type:TYPE_INCELL_MAP}];
-          int32 target = 10 [(tableau.field) = {name:"Target"}];
-        }
-        Info warn_info = 7 [(tableau.field) = {name:"WarnInfo" type:TYPE_INCELL_STRUCT}];
-        message Info {
-          int32 id = 1 [(tableau.field) = {name:"Id"}];
-          string desc = 2 [(tableau.field) = {name:"Desc"}];
-          int32 value = 3 [(tableau.field) = {name:"Value"}];
-        }
-        Info incell_struct = 8 [(tableau.field) = {name:"IncellStruct" type:TYPE_INCELL_STRUCT}];
-        repeated int32 int_params = 9 [(tableau.field) = {name:"IntParams" type:TYPE_INCELL_LIST}];
-        map<int32, string> tips = 10 [(tableau.field) = {name:"Tips" type:TYPE_INCELL_MAP}];
-        repeated string client_param_list = 11 [(tableau.field) = {name:"ClientParam" layout:LAYOUT_HORIZONTAL}];
-      }
+    uint32 id = 1 [(tableau.field) = {name:"ID"}];
+    string name = 2 [(tableau.field) = {name:"Name"}];
+  }
+  map<uint32, Chapter> chapter_map = 2 [(tableau.field) = {name:"Chapter" key:"ID" layout:LAYOUT_VERTICAL}];
+  message Chapter {
+    uint32 id = 1 [(tableau.field) = {name:"ID"}];
+    string name = 2 [(tableau.field) = {name:"Name"}];
+  }
+  repeated Section section_list = 3 [(tableau.field) = {name:"Section" layout:LAYOUT_VERTICAL}];
+  message Section {
+    uint32 id = 1 [(tableau.field) = {name:"ID"}];
+    string name = 2 [(tableau.field) = {name:"Name"}];
+    repeated Item item_list = 3 [(tableau.field) = {name:"Item" layout:LAYOUT_HORIZONTAL}];
+    message Item {
+      int32 id = 1 [(tableau.field) = {name:"Id"}];
+      int32 num = 2 [(tableau.field) = {name:"Num"}];
     }
   }
+  Duration duration = 4 [(tableau.field) = {name:"Duration"}];
+  google.protobuf.Duration timespan = 5 [(tableau.field) = {name:"Timespan"}];
+  Task task = 6 [(tableau.field) = {name:"Task"}];
+  message Task {
+    int32 type = 1 [(tableau.field) = {name:"Type"}];
+    int32 param_1 = 2 [(tableau.field) = {name:"Param1"}];
+    int32 param_2 = 3 [(tableau.field) = {name:"Param2"}];
+    int32 param_3 = 4 [(tableau.field) = {name:"Param3"}];
+    repeated int32 param_list_1 = 5 [(tableau.field) = {name:"ParamList1" type:TYPE_INCELL_LIST}];
+    repeated int32 param_list_2 = 6 [(tableau.field) = {name:"ParamList2" type:TYPE_INCELL_LIST}];
+    repeated int32 param_list_3 = 7 [(tableau.field) = {name:"ParamList3" type:TYPE_INCELL_LIST}];
+    map<int32, int32> param_map_1 = 8 [(tableau.field) = {name:"ParamMap1" type:TYPE_INCELL_MAP}];
+    map<int32, int32> param_map_2 = 9 [(tableau.field) = {name:"ParamMap2" type:TYPE_INCELL_MAP}];
+    int32 target = 10 [(tableau.field) = {name:"Target"}];
+  }
+  Info warn_info = 7 [(tableau.field) = {name:"WarnInfo" type:TYPE_INCELL_STRUCT}];
+  message Info {
+    int32 id = 1 [(tableau.field) = {name:"Id"}];
+    string desc = 2 [(tableau.field) = {name:"Desc"}];
+    int32 value = 3 [(tableau.field) = {name:"Value"}];
+  }
+  Info incell_struct = 8 [(tableau.field) = {name:"IncellStruct" type:TYPE_INCELL_STRUCT}];
+  repeated int32 int_params = 9 [(tableau.field) = {name:"IntParams" type:TYPE_INCELL_LIST}];
+  map<int32, string> tips = 10 [(tableau.field) = {name:"Tips" type:TYPE_INCELL_MAP}];
+  repeated string client_param_list = 11 [(tableau.field) = {name:"ClientParam" layout:LAYOUT_HORIZONTAL}];
 }
 
 message ExchangeInfo {
diff --git a/test/protoconf/xml/feature_toggle.proto b/test/protoconf/xml/feature_toggle.proto
index b876e51..fed20db 100644
--- a/test/protoconf/xml/feature_toggle.proto
+++ b/test/protoconf/xml/feature_toggle.proto
@@ -12,27 +12,24 @@ option (tableau.workbook) = {name:"FeatureToggle.xml"};
 message ToggleCfg {
   option (tableau.worksheet) = {name:"ToggleCfg" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
 
-  int32 tableau_placeholder = 1 [(tableau.field) = {name:"TableauPlaceholder"}];
-  map<uint32, FeatureToggle> feature_toggle_map = 2 [(tableau.field) = {name:"FeatureToggle" key:"EnvID" layout:LAYOUT_VERTICAL}];
+  map<uint32, FeatureToggle> feature_toggle_map = 1 [(tableau.field) = {name:"FeatureToggle" key:"EnvID" layout:LAYOUT_VERTICAL}];
   message FeatureToggle {
     uint32 env_id = 1 [(tableau.field) = {name:"EnvID"}];
     string desc = 2 [(tableau.field) = {name:"Desc"}];
     map<int32, Toggle> toggle_map = 3 [(tableau.field) = {name:"Toggle" key:"Id" layout:LAYOUT_VERTICAL}];
     message Toggle {
       FeatureToggleMacroType id = 1 [(tableau.field) = {name:"Id"}];
-      int32 open_rate = 2 [(tableau.field) = {name:"OpenRate"}];
-      uint32 world_id = 3 [(tableau.field) = {name:"WorldID"}];
-      uint32 zone_id = 4 [(tableau.field) = {name:"ZoneID"}];
-      google.protobuf.Timestamp open_time = 5 [(tableau.field) = {name:"OpenTime"}];
-      google.protobuf.Timestamp close_time = 6 [(tableau.field) = {name:"CloseTime"}];
-      int32 user_limit_type = 7 [(tableau.field) = {name:"UserLimitType"}];
-      int32 user_source_type = 8 [(tableau.field) = {name:"UserSourceType"}];
-      int64 sys_key_1 = 9 [(tableau.field) = {name:"SysKey1"}];
-      int64 sys_key_2 = 10 [(tableau.field) = {name:"SysKey2"}];
-      int64 sys_key_3 = 11 [(tableau.field) = {name:"SysKey3"}];
-      bool notify_closed = 12 [(tableau.field) = {name:"NotifyClosed"}];
-      string name = 13 [(tableau.field) = {name:"Name"}];
-      string desc = 14 [(tableau.field) = {name:"Desc"}];
+      uint32 world_id = 2 [(tableau.field) = {name:"WorldID"}];
+      uint32 zone_id = 3 [(tableau.field) = {name:"ZoneID"}];
+      google.protobuf.Timestamp open_time = 4 [(tableau.field) = {name:"OpenTime"}];
+      google.protobuf.Timestamp close_time = 5 [(tableau.field) = {name:"CloseTime"}];
+      int64 sys_key_1 = 6 [(tableau.field) = {name:"SysKey1"}];
+      int64 sys_key_2 = 7 [(tableau.field) = {name:"SysKey2"}];
+      bool notify_closed = 8 [(tableau.field) = {name:"NotifyClosed"}];
+      string name = 9 [(tableau.field) = {name:"Name"}];
+      int32 open_rate = 10 [(tableau.field) = {name:"OpenRate"}];
+      string desc = 11 [(tableau.field) = {name:"Desc"}];
     }
   }
+  bool _placeholder = 2 [(tableau.field) = {name:"_placeholder"}];
 }
diff --git a/test/protoconf/xml/match__match.proto b/test/protoconf/xml/match__match.proto
index 0381f2f..2fd24e8 100644
--- a/test/protoconf/xml/match__match.proto
+++ b/test/protoconf/xml/match__match.proto
@@ -13,16 +13,15 @@ option (tableau.workbook) = {name:"match/Match.xml"};
 message MatchCfg {
   option (tableau.worksheet) = {name:"MatchCfg" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
 
-  int32 tableau_placeholder = 1 [(tableau.field) = {name:"TableauPlaceholder"}];
-  ConfirmTimeOutPunish confirm_time_out_punish = 2 [(tableau.field) = {name:"ConfirmTimeOutPunish"}];
+  ConfirmTimeOutPunish confirm_time_out_punish = 1 [(tableau.field) = {name:"ConfirmTimeOutPunish"}];
   message ConfirmTimeOutPunish {
     google.protobuf.Duration time = 1 [(tableau.field) = {name:"Time"}];
   }
-  MatchStatInfo match_stat_info = 3 [(tableau.field) = {name:"MatchStatInfo"}];
+  MatchStatInfo match_stat_info = 2 [(tableau.field) = {name:"MatchStatInfo"}];
   message MatchStatInfo {
     google.protobuf.Duration report_interval = 1 [(tableau.field) = {name:"ReportInterval"}];
   }
-  map<int32, MatchMode> match_mode_map = 4 [(tableau.field) = {name:"MatchMode" key:"MissionType" layout:LAYOUT_VERTICAL}];
+  map<int32, MatchMode> match_mode_map = 3 [(tableau.field) = {name:"MatchMode" key:"MissionType" layout:LAYOUT_VERTICAL}];
   message MatchMode {
     MissionType mission_type = 1 [(tableau.field) = {name:"MissionType"}];
     string desc = 2 [(tableau.field) = {name:"Desc"}];
@@ -30,8 +29,8 @@ message MatchCfg {
     message Time {
       google.protobuf.Duration min_time = 1 [(tableau.field) = {name:"MinTime"}];
       google.protobuf.Duration max_time = 2 [(tableau.field) = {name:"MaxTime"}];
-      int32 freq = 3 [(tableau.field) = {name:"Freq"}];
-      google.protobuf.Duration confirm_time = 4 [(tableau.field) = {name:"ConfirmTime"}];
+      google.protobuf.Duration confirm_time = 3 [(tableau.field) = {name:"ConfirmTime"}];
+      int32 freq = 4 [(tableau.field) = {name:"Freq"}];
     }
     Open open = 4 [(tableau.field) = {name:"Open"}];
     message Open {
@@ -60,8 +59,8 @@ message MatchCfg {
     message Filter {
       ENMMatchFilter filter_id = 1 [(tableau.field) = {name:"FilterID"}];
       bool open = 2 [(tableau.field) = {name:"Open"}];
-      int32 value = 3 [(tableau.field) = {name:"Value"}];
-      google.protobuf.Duration upd_interval = 4 [(tableau.field) = {name:"UpdInterval"}];
+      google.protobuf.Duration upd_interval = 3 [(tableau.field) = {name:"UpdInterval"}];
+      int32 value = 4 [(tableau.field) = {name:"Value"}];
       string desc = 5 [(tableau.field) = {name:"Desc"}];
       repeated Power power_list = 6 [(tableau.field) = {name:"Power" key:"HornorPower" layout:LAYOUT_VERTICAL}];
       message Power {
@@ -72,12 +71,7 @@ message MatchCfg {
         }
       }
     }
-    Route route = 9 [(tableau.field) = {name:"Route"}];
-    message Route {
-      int32 key = 1 [(tableau.field) = {name:"Key"}];
-      string desc = 2 [(tableau.field) = {name:"Desc"}];
-    }
-    MatchAI match_ai = 10 [(tableau.field) = {name:"MatchAI"}];
+    MatchAI match_ai = 9 [(tableau.field) = {name:"MatchAI"}];
     message MatchAI {
       bool is_open = 1 [(tableau.field) = {name:"IsOpen"}];
       bool player_only_one_camp = 2 [(tableau.field) = {name:"PlayerOnlyOneCamp"}];
@@ -91,23 +85,24 @@ message MatchCfg {
         string desc = 5 [(tableau.field) = {name:"Desc"}];
       }
     }
-    repeated Random random_list = 11 [(tableau.field) = {name:"Random" key:"MissionType" layout:LAYOUT_VERTICAL}];
+    repeated Random random_list = 10 [(tableau.field) = {name:"Random" key:"MissionType" layout:LAYOUT_VERTICAL}];
     message Random {
       MissionType mission_type = 1 [(tableau.field) = {name:"MissionType"}];
-      int32 priority = 2 [(tableau.field) = {name:"Priority"}];
+    }
+    Route route = 11 [(tableau.field) = {name:"Route"}];
+    message Route {
+      int32 key = 1 [(tableau.field) = {name:"Key"}];
+      string desc = 2 [(tableau.field) = {name:"Desc"}];
     }
   }
+  bool _placeholder = 4 [(tableau.field) = {name:"_placeholder"}];
   MatchRoom match_room = 5 [(tableau.field) = {name:"MatchRoom"}];
   message MatchRoom {
     int32 max_loop = 1 [(tableau.field) = {name:"MaxLoop"}];
   }
-  MatchPlayer match_player = 6 [(tableau.field) = {name:"MatchPlayer"}];
-  message MatchPlayer {
-    int32 max_loop = 1 [(tableau.field) = {name:"MaxLoop"}];
-  }
-  TeamRatingWeight team_rating_weight = 7 [(tableau.field) = {name:"TeamRatingWeight"}];
+  TeamRatingWeight team_rating_weight = 6 [(tableau.field) = {name:"TeamRatingWeight"}];
   message TeamRatingWeight {
-    int32 tableau_placeholder = 1 [(tableau.field) = {name:"TableauPlaceholder"}];
+    bool _placeholder = 1 [(tableau.field) = {name:"_placeholder"}];
     repeated Weight weight_list = 2 [(tableau.field) = {name:"Weight" key:"Num" layout:LAYOUT_VERTICAL}];
     message Weight {
       int32 num = 1 [(tableau.field) = {name:"Num"}];
diff --git a/test/protoconf/xml/match__simple_match_conf.proto b/test/protoconf/xml/match__simple_match_conf.proto
new file mode 100644
index 0000000..a5297a7
--- /dev/null
+++ b/test/protoconf/xml/match__simple_match_conf.proto
@@ -0,0 +1,34 @@
+// Generated by tableauc 0.1.1. DO NOT EDIT.
+syntax = "proto3";
+package testxml;
+option go_package = "github.com/tableauio/tableau/cmd/test/testpb/xml";
+
+import "tableau/protobuf/tableau.proto";
+
+option (tableau.workbook) = {name:"match/SimpleMatchConf.xml"};
+
+message SimpleMatchConf {
+  option (tableau.worksheet) = {name:"SimpleMatchConf" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+
+  bool _placeholder = 1 [(tableau.field) = {name:"_placeholder"}];
+  ConfirmTimeOutPunish confirm_time_out_punish = 2 [(tableau.field) = {name:"ConfirmTimeOutPunish"}];
+  message ConfirmTimeOutPunish {
+    string time = 1 [(tableau.field) = {name:"Time"}];
+  }
+  MatchRoom match_room = 3 [(tableau.field) = {name:"MatchRoom"}];
+  message MatchRoom {
+    int32 max_loop = 1 [(tableau.field) = {name:"MaxLoop"}];
+  }
+  TeamRatingWeight team_rating_weight = 4 [(tableau.field) = {name:"TeamRatingWeight"}];
+  message TeamRatingWeight {
+    bool _placeholder = 1 [(tableau.field) = {name:"_placeholder"}];
+    repeated Weight weight_list = 2 [(tableau.field) = {name:"Weight" key:"Num" layout:LAYOUT_VERTICAL}];
+    message Weight {
+      int32 num = 1 [(tableau.field) = {name:"Num"}];
+      repeated Param param_list = 2 [(tableau.field) = {name:"Param" key:"Value" layout:LAYOUT_VERTICAL}];
+      message Param {
+        int32 value = 1 [(tableau.field) = {name:"Value"}];
+      }
+    }
+  }
+}
diff --git a/test/protoconf/xml/multi_sheet.proto b/test/protoconf/xml/multi_sheet.proto
new file mode 100644
index 0000000..7ebab17
--- /dev/null
+++ b/test/protoconf/xml/multi_sheet.proto
@@ -0,0 +1,30 @@
+// Generated by tableauc 0.1.1. DO NOT EDIT.
+syntax = "proto3";
+package testxml;
+option go_package = "github.com/tableauio/tableau/cmd/test/testpb/xml";
+
+import "tableau/protobuf/tableau.proto";
+
+option (tableau.workbook) = {name:"MultiSheet.xml"};
+
+message Server {
+  option (tableau.worksheet) = {name:"Server" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+
+  string desc = 1 [(tableau.field) = {name:"Desc"}];
+  repeated Node node_list = 2 [(tableau.field) = {name:"Node" key:"Addr" layout:LAYOUT_VERTICAL}];
+  message Node {
+    string addr = 1 [(tableau.field) = {name:"Addr"}];
+    int32 proc_num = 2 [(tableau.field) = {name:"ProcNum"}];
+  }
+}
+
+message Client {
+  option (tableau.worksheet) = {name:"Client" namerow:1 typerow:2 noterow:3 datarow:4 nameline:1 typeline:1 nested:true};
+
+  bool _placeholder = 1 [(tableau.field) = {name:"_placeholder"}];
+  Version version = 2 [(tableau.field) = {name:"Version"}];
+  message Version {
+    string min = 1 [(tableau.field) = {name:"Min"}];
+    string max = 2 [(tableau.field) = {name:"Max"}];
+  }
+}
diff --git a/test/tableau_test.go b/test/tableau_test.go
index 1fb049c..d458f31 100644
--- a/test/tableau_test.go
+++ b/test/tableau_test.go
@@ -10,7 +10,6 @@ import (
 	"github.com/tableauio/tableau/load"
 	"github.com/tableauio/tableau/options"
 	"github.com/tableauio/tableau/test/testpb/excel"
-	_ "github.com/tableauio/tableau/test/testpb/excel"
 	_ "github.com/tableauio/tableau/test/testpb/xml"
 )
 
diff --git a/test/testdata/excel/Test#@TABLEAU.csv b/test/testdata/excel/Test#@TABLEAU.csv
index 566bd2d..a285394 100644
--- a/test/testdata/excel/Test#@TABLEAU.csv
+++ b/test/testdata/excel/Test#@TABLEAU.csv
@@ -1,5 +1,5 @@
-Sheet,Alias,Nameline,Typeline,Nested
-Activity,,1,1,
-Exchange,ExchangeInfo,0,0,
-Match,MatchConf,1,1,true
-Loader,,1,1,true
+Sheet,Alias,Nameline,Typeline,Nested,OrderedMap,Sep
+Activity,,1,1,,true,","
+Exchange,ExchangeInfo,0,0,,,
+Match,MatchConf,1,1,true,,
+Loader,,1,1,true,,
diff --git a/test/testdata/xml/ClientConf.xml b/test/testdata/xml/ClientConf.xml
new file mode 100644
index 0000000..aa1c5be
--- /dev/null
+++ b/test/testdata/xml/ClientConf.xml
@@ -0,0 +1,3 @@
+<ClientCfg>
+    <Version Min="1.0.0.1" Max="1.0.0.3"/>
+</ClientCfg>
\ No newline at end of file
diff --git a/test/testdata/xml/FeatureToggle.xml b/test/testdata/xml/FeatureToggle.xml
index a0b7648..ed47219 100644
--- a/test/testdata/xml/FeatureToggle.xml
+++ b/test/testdata/xml/FeatureToggle.xml
@@ -1,15 +1,14 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<ToggleCfg>
-    <!-- 添加新特性开关后，调用tools/gen_feature_toggle/search_all.py 生成代码 -->
-
-    <@TABLEAU>
-        <FeatureToggle EnvID="map<uint32,FeatureToggle>" desc="string">
-            <Toggle ID="map<enum<.FeatureToggleMacroType>,Toggle>" OpenRate="int32" WorldID="uint32" ZoneID="uint32" OpenTime="datetime|2022-01-23 15:40:00"  CloseTime="datetime|2022-01-23 15:40:00" UserLimitType="int32" UserSourceType="int32" SysKey1="int64" SysKey2="int64" SysKey3="int64" NotifyClosed="bool" />
+<@TABLEAU>
+    <ToggleCfg>
+        <FeatureToggle EnvID="map<uint32,FeatureToggle>">
+            <Toggle ID="map<enum<.FeatureToggleMacroType>,Toggle>" WorldID="uint32" ZoneID="uint32" OpenTime="datetime|2022-01-23 15:40:00" CloseTime="datetime|2022-01-23 15:40:00" SysKey1="int64" SysKey2="int64" NotifyClosed="bool"/>
         </FeatureToggle>
-    </@TABLEAU>
+    </ToggleCfg>
+</@TABLEAU>
 
+<ToggleCfg>
     <FeatureToggle EnvID="0" desc="common: 通用环境的配置，需要包含全量的开关。common没有且对应环境也没有的开关会被默认为关闭。">
-
         <!--
         - ID				系统对应的toggleid
         - OpenRate			开放比例[0,10000], 0表示关闭，10000全开
@@ -17,8 +16,6 @@
         - ZoneID			小区id, 0表示所有小区，支持','分隔配置多个，示例: 1,2；支持'-'配置连续大区,示例: 1-2 相当于配置1,2两个小区
         - OpenTime			开启时间，逗号分隔日期和时间，示例：20190101, 0 表示20190101 0点开启; 20190101, 100000 表示20190101 10点0分0秒开启
         - CloseTime			关闭时间，逗号分隔日期和时间，示例：20190101, 0 表示20190101 0点关闭ui9; 20190101, 100000 表示20190101 10点0分0秒关闭
-        - UserLimitType		限制用户类型，0表示不限制，1限制账号uid，2限制账号openid，3限制角色
-        - UserSourceType	限制用户名单类型，0表示白名单，1表示黑名单
         - SysKey1-3			系统自定义筛选key
         - NotifyClosed		开关关闭是否通知客户端，0不通知，其他通知
         -->
@@ -32,50 +29,6 @@
         <Toggle name="意志进阶" ID="TOGGLE_WEAPON_STAR_UP" OpenRate="10000" SysKey1="0" desc="SysKey1->EquipID"/>
         <Toggle name="创建队伍" ID="TOGGLE_TEAM_CREATE_TEAM" OpenRate="10000" />
         <Toggle name="队伍匹配" ID="TOGGLE_TEAM_REQ_MATCH" OpenRate="10000" />
-        <!-- 客户端 暂时没有主动加入队伍的功能，服务器也暂时关闭 -->
-        <Toggle name="加入队伍" ID="TOGGLE_TEAM_JOIN_TEAM" OpenRate="0" />
-        <Toggle name="悬赏解锁" ID="TOGGLE_BOUNTY_UNLOCK" OpenRate="0" />
-        <Toggle name="排行榜" ID="TOGGLE_RANK" OpenRate="10000" />
-        <Toggle name="商城" ID="TOGGLE_MALL_SHOP" OpenRate="10000" />
-        <Toggle name="商城购买" ID="TOGGLE_MALL_BUY" OpenRate="10000" />
-        <Toggle name="聊天" ID="TOGGLE_CHAT" OpenRate="10000" desc="SysKey1->ChatStrategy SysKey2->ChatType"/>
-        <Toggle name="全服邮件" ID="TOGGLE_GLOBAL_MAIL" OpenRate="10000" />
-        <Toggle name="援护技" ID="TOGGLE_ASSIST_SKILL" OpenRate="0" />
-        <Toggle name="单局统计流水" ID="TOGGLE_GAMEPLAY_STATISTIC" OpenRate="10000" />
-
-        <Toggle name="是否要校验版本降级登录" ID="TOGGLE_CHECK_CLIENT_VERSION_INCR" OpenRate="0" />
-        <Toggle name="点券pc支付开放开关" ID="TOGGLE_PC_PAY" OpenRate="10000" />
-        <Toggle name="Tcaplus状态统计开关" ID="TOGGLE_TCAP_CMD_STAT" OpenRate="10000" />
-        <Toggle name="添加道具，背包满是否当错误" ID="TOGGLE_ITEM_BAG_FULL_IS_FAIL" OpenRate="10000" />
-        <Toggle name="压测 跳过加道具" ID="TOGGLE_ITEM_PRESSURE_BREAK_ADDOBJ" OpenRate="10000" />
-        <Toggle name="开发配置切片, 非0表示使用切片" ID="TOGGLE_OPEN_CFG_SET" OpenRate="10000" />
-        <Toggle name="单局开始未连上服务器，是否当做掉线，非0表示当做掉线" ID="TOGGLE_GAMEPLAY_START_NOT_CONNECT" OpenRate="10000" />
-        <Toggle name="pvpagentsvr是否开启共享内存恢复" ID="TOGGLE_PVPAGENT_USE_RESUME" OpenRate="10000" />
-        <Toggle name="chatsvr是否开启共享内存恢复" ID="TOGGLE_CHAT_USE_RESUME" OpenRate="10000" />
-        <Toggle name="viewsvr是否开启共享内存恢复" ID="TOGGLE_VIEW_USE_RESUME" OpenRate="0" />
-        <Toggle name="主动离开副本是否恢复" ID="TOGGLE_LEAVE_FUBEN_NEED_RESUME" OpenRate="0" />
-        <Toggle name="副本断线重连测试开关" ID="TOGGLE_FUBEN_RECONNCET" OpenRate="10000" />
-        <Toggle name="副本MD5校验开关" ID="TOGGLE_FUBEN_MD5_CHECK" OpenRate="0" />
-        <Toggle name="资源版本检查开发" ID="TOGGLE_HOTRES_CHECK" OpenRate="0" />
-        <Toggle name="匹配是否需要检查版本一致性" ID="TOGGLE_MATCH_VERSION_CHECK" OpenRate="0" />
-        <Toggle name="tlog区分多套ce环境【上线后关掉】" ID="TOGGLE_SUPORT_MUTLI_CE_TLOG" OpenRate="10000" />
-        <Toggle name="日志是否使用实时时间" ID="TOGGLE_LOG_REAL_TIME" OpenRate="0" />
-        <Toggle name="是否开启PvpOpenTime限制，请见PvpOpenTime.xlsx" ID="TOGGLE_PVP_OPEN_TIME" OpenRate="10000" />
-        <Toggle name="是否开启FubenOpenTime限制，请见FubenOpenTimeInfo.xlsx" ID="TOGGLE_FUBEN_OPEN_TIME" OpenRate="10000" />
-        <Toggle name="开局是否检查已经有单局" ID="TOGGLE_IN_GAME_CHECK" OpenRate="10000" />
-
-        <Toggle name="会员、超会入口" ID="TOGGLE_VIP_ENTRY" OpenRate="10000" />
-        <Toggle name="邀请" ID="TOGGLE_INVITE" OpenRate="10000" />
-        <Toggle name="游戏特权" ID="TOGGLE_GAME_PRIVILEGE" OpenRate="10000" />
-        <Toggle name="潘多拉开关" ID="TOGGLE_PANDORA" OpenRate="10000" />
-        <Toggle name="微社区入口开关" ID="TOGGLE_MICRO_COMMUNITY" OpenRate="10000" />
-        <Toggle name="企鹅电竞" ID="TOGGLE_TENCENT_ESPORT" OpenRate="10000" />
-        <Toggle name="心悦特权" ID="TOGGLE_XINYUE_PRIVILEGE" OpenRate="10000" />
-        <Toggle name="微信公众号" ID="TOGGLE_WEIXIN_MEDIA_PLATFORM" OpenRate="10000" />
-        <Toggle name="礼包中心" ID="TOGGLE_PACKAGE_CENTER" OpenRate="10000" />
-        <Toggle name="公告(包括:登录前公告+登录后公告)" ID="TOGGLE_ANNOUNCEMENT" OpenRate="10000" />
-        <Toggle name="活动入口" ID="TOGGLE_ACTIVITY_ENTRY" OpenRate="10000" />
-        <Toggle name="是否开启录像上传" ID="TOGGLE_UPLOAD_VIDEO" OpenRate="10000" />
     </FeatureToggle>
 
     <FeatureToggle EnvID="21" desc="审核服: 以下开关配置只会在审核服生效，会覆盖上面common的配置。">
diff --git a/test/testdata/xml/MultiSheet.xml b/test/testdata/xml/MultiSheet.xml
new file mode 100644
index 0000000..1fe8551
--- /dev/null
+++ b/test/testdata/xml/MultiSheet.xml
@@ -0,0 +1,19 @@
+<@TABLEAU>
+    <Server/>
+    <Client/>
+</@TABLEAU>
+
+<Server desc="test">
+    <Node Addr="9.123.134.1" ProcNum="2"/>
+    <Node Addr="9.168.134.1" ProcNum="5"/>
+</Server>
+
+<Client>
+    <Version Min="1.0.0.1" Max="1.0.0.3"/>
+</Client>
+
+<TrashBin>
+    <Trash Name="Banana"/>
+    <Trash Name="Apple"/>
+    <Trash Name="Bottle"/>
+</TrashBin>
\ No newline at end of file
diff --git a/test/testdata/xml/match/Match.xml b/test/testdata/xml/match/Match.xml
index e863c7e..63066a9 100644
--- a/test/testdata/xml/match/Match.xml
+++ b/test/testdata/xml/match/Match.xml
@@ -1,34 +1,30 @@
-<MatchCfg>
-    <@TABLEAU>
+<@TABLEAU>
+    <MatchCfg>
         <ConfirmTimeOutPunish Time="duration" />
         <MatchStatInfo ReportInterval="duration"/>
-        <MatchMode MissionType="map<enum<.MissionType>,MatchMode>" desc="string" >
-            <Time MinTime="duration" MaxTime="duration" Freq="int32" ConfirmTime="duration" />
-            <Open BeginDate="date" BeginTime="time"  EndDate="date" EndTime="time" />
-            <Camp CampID="[Camp]<uint32>" PlayerNum="int32" desc="string" />
-            <Strategy StrategyID="enum<.ENMMatchStrategy>" desc="string" />
-
-            <Scope    ScopeID="enum<.ENMMatchScope>"  desc="string" />
-            <Filter FilterID="[Filter]<enum<.ENMMatchFilter>>" Open="bool" Value="int32"  UpdInterval="duration" desc="string">
+        <MatchMode MissionType="map<enum<.MissionType>,MatchMode>">
+            <Time MinTime="duration" MaxTime="duration" ConfirmTime="duration" />
+            <Open BeginDate="date" BeginTime="time" EndDate="date" EndTime="time" />
+            <Camp CampID="[Camp]<uint32>" />
+            <Strategy StrategyID="enum<.ENMMatchStrategy>" />
+
+            <Scope ScopeID="enum<.ENMMatchScope>" />
+            <Filter FilterID="[Filter]<enum<.ENMMatchFilter>>" Open="bool" UpdInterval="duration">
                 <Power HornorPower="[Power]<int32>">
                     <Param Value="[Param]<int32>"/>
                 </Power>
             </Filter>
-            <Route    Key="int32" desc="string" />
-            <!-- PlayerOnlyOneCamp: 匹配到AI的玩家都在一个阵营 GuaranteeAIRatio:保底AI概率万分比 -->
-            <MatchAI IsOpen="bool" PlayerOnlyOneCamp="bool" GuaranteeAIRatio="int32">
-                <AI Type="[AI]<enum<.ENMAIWarmType>>" IsOpen="bool" MinTime="duration" MaxTime="duration"  desc="string" />
+            <MatchAI IsOpen="bool" PlayerOnlyOneCamp="bool">
+                <AI Type="[AI]<enum<.ENMAIWarmType>>" IsOpen="bool" MinTime="duration" MaxTime="duration" />
             </MatchAI>
-            <!-- 优先级, 随机模式随机先后顺序的权重，用于控制随机模式各个模式的比例 -->
-            <Random MissionType="[Random]<enum<.MissionType>>" Priority="int32" />
+            <Random MissionType="[Random]<enum<.MissionType>>" />
         </MatchMode>
-    </@TABLEAU>
+    </MatchCfg>
+</@TABLEAU>
 
-    <!-- 匹配确认超时惩罚时间 -->
+<MatchCfg>
     <ConfirmTimeOutPunish Time="2s" />
     <MatchRoom MaxLoop="100" />
-    <MatchPlayer MaxLoop="100" />
-    <MatchStatInfo ReportInterval="5s"/>
 
     <TeamRatingWeight>
         <Weight Num="1">
@@ -38,738 +34,35 @@
             <Param Value="30"/>
             <Param Value="70"/>
         </Weight>
-        <Weight Num="3">
-            <Param Value="20"/>
-            <Param Value="30"/>
-            <Param Value="50"/>
-        </Weight>
     </TeamRatingWeight>
 
     <MatchMode MissionType="MT_PVP_Low_Rank_RoundBattle_1V1" desc="排位赛">
-        <!-- 之前的“低分段排位赛”，当前版本实际使用的排位赛MissionType-->
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="15s" /> <!-- MaxTime是最长匹配时间 ConfirmTime是等待确认时间 -->
-        <Open BeginDate="20200506" BeginTime="000000"  EndDate="20500506" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_Glicko2" desc="匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxRatingGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="30s" MaxTime="50s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="false" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-
-    <MatchMode MissionType="MT_PVP_SUPERKILL_MULTI" desc="竞技2V2">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="2" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="2" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_VersionConsistent" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxMMRGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="true" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="false" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="false" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="false" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-   <MatchMode MissionType="MT_PVP_SUPERKILL" desc="竞技1V1">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_VersionConsistent" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxMMRGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-
-   <MatchMode MissionType="MT_PVP_ELIMINATION_GAME" desc="1v1v1v1 多人淘汰赛-乱斗玩法">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次 ConfirmTime临时调整0，这个不为0，客户端目前单局流程进不去-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Camp     CampID="2" PlayerNum="1" desc="阵营3" />
-        <Camp     CampID="3" PlayerNum="1" desc="阵营4" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_VersionConsistent" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxMMRGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="10000">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="false" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="false" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-    <MatchMode MissionType="MT_PVP_TEAM_DEATH_MATCH" desc="匹配模式 团竞PVP-4v4玩法">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20200506" BeginTime="000000"  EndDate="20500506" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="4" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="4" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_Unify" desc="无差别匹配" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="0" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="false" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="60s" MaxTime="80s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-
-    <MatchMode MissionType="MT_ARCADE" desc="混沌裂隙 AI挑战">
-        <!-- AI挑战 无需确认匹配 不等待-->
-        <Time MinTime="0s" MaxTime="90s" Freq="0" ConfirmTime="0s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_VersionConsistent" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="0s" MaxTime="0s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="0s" MaxTime="0s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="0s" MaxTime="0s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="0s" MaxTime="0s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="0s" MaxTime="0s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="0s" MaxTime="0s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-   <MatchMode MissionType="MT_PVP_SUPERKILL_GVG" desc="战队赛1V1">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20501220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_VersionConsistent" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxMMRGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Filter   FilterID="MatchFilter_DiffGuild" Open="true" Value="0"  desc="相同战队不匹配"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-   <MatchMode MissionType="MT_PVP_SUPERKILL_FRIENDPK" desc="约战1V1">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_VersionConsistent" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxMMRGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-    <MatchMode MissionType="MT_PVP_SUPERKILL_MULTI_FRIENDPK" desc="约战2V2">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="2" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="2" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxMMRGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="true" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="false" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="30s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="false" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="false" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-    <MatchMode MissionType="MT_PVP_TEAM_DEATH_MATCH_FRIENDPK" desc="约战4v4">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20200506" BeginTime="000000"  EndDate="20500506" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="4" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="4" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_Unify" desc="无差别匹配" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="0" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="false" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="60s" MaxTime="80s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-   <MatchMode MissionType="MT_PVP_ELIMINATION_GAME_FRIENDPK" desc="约战1v1v1v1">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次 ConfirmTime临时调整0，这个不为0，客户端目前单局流程进不去-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Camp     CampID="2" PlayerNum="1" desc="阵营3" />
-        <Camp     CampID="3" PlayerNum="1" desc="阵营4" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_VersionConsistent" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxMMRGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="10000">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="30s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="false" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="false" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-
-   <MatchMode MissionType="MT_PVP_RANDOM" desc="混匹">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次 ConfirmTime临时调整0，这个不为0，客户端目前单局流程进不去-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="10" desc="阵营1" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_VersionConsistent" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxMMRGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="10000">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="30s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="false" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="false" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-        <Random MissionType="MT_PVP_SUPERKILL_MULTI" Priority="100" />
-        <Random MissionType="MT_PVP_SUPERKILL" Priority="100" />
-        <Random MissionType="MT_PVP_ELIMINATION_GAME" Priority="100" />
-        <Random MissionType="MT_PVP_Low_Rank_RoundBattle_1V1" Priority="100" />
-    </MatchMode>
-
-
-    <MatchMode MissionType="MT_PVP_SUPERKILL_COMPETITION" desc="比赛1V1">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_VersionConsistent" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxMMRGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="false" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="false" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="false" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="false" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="false" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-    <MatchMode MissionType="MT_PVP_SUPERKILL_MULTI_COMPETITION" desc="比赛2V2">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="2" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="2" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxMMRGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="false" PlayerOnlyOneCamp="true" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="false" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="false" MinTime="20s" MaxTime="30s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="false" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="false" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-    <MatchMode MissionType="MT_PVP_TEAM_DEATH_MATCH_COMPETITION" desc="比赛4v4">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20200506" BeginTime="000000"  EndDate="20500506" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="4" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="4" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_Unify" desc="无差别匹配" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="0" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="false" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="false" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="false" MinTime="60s" MaxTime="80s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="false" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="false" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-   <MatchMode MissionType="MT_PVP_ELIMINATION_GAME_COMPETITION" desc="比赛1v1v1v1">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次 ConfirmTime临时调整0，这个不为0，客户端目前单局流程进不去-->
-        <Time MinTime="1s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Camp     CampID="2" PlayerNum="1" desc="阵营3" />
-        <Camp     CampID="3" PlayerNum="1" desc="阵营4" />
-        <Strategy StrategyID="MatchStrategy_MMR" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_VersionConsistent" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxMMRGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MatchVersion" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_TrySameCluster" Open="true" Value="5"  desc="就近匹配filter"/>
-        <Route    Key="1" desc="路由哈希值" />
-        <MatchAI IsOpen="false" PlayerOnlyOneCamp="false" GuaranteeAIRatio="10000">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="false" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="false" MinTime="20s" MaxTime="30s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="false" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="false" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-
-
-
-
-
-
-    <!--············································-->
-    <!--·············································-->
-    <!-- 以下玩法为已废弃的旧玩法，匹配配置先保留，但后置-->
-
-   <MatchMode MissionType="MT_CLASSIC_PVP_HeadCount_1V1" desc="旧1V1-已废弃">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
         <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_RoleHonorPower" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_VersionConsistent" Open="true" Value="0"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxHornorPowerGap" Open="false" UpdInterval="10s"  desc="荣耀战力匹配过滤器">
-            <Power HornorPower="300">
-                <Param Value="100"/>
-                <Param Value="200"/>
-            </Power>
-            <Power HornorPower="600">
-                <Param Value="130"/>
-                <Param Value="300"/>
-            </Power>
-            <Power HornorPower="1000">
-                <Param Value="150"/>
-                <Param Value="500"/>
-            </Power>
-            <Power HornorPower="99999">
-                <Param Value="200"/>
-                <Param Value="600"/>
-            </Power>
-        </Filter>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-   <MatchMode MissionType="MT_PVP_RoundBattle_1V1" desc="匹配模式  车轮战1v1模式-已废弃">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="15s" /> <!-- MaxTime是最长匹配时间 ConfirmTime是等待确认时间 -->
-        <Open BeginDate="20200506" BeginTime="000000"  EndDate="20500506" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_RoleHonorPower" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxHornorPowerGap" Open="true" UpdInterval="10s"  desc="荣耀战力匹配过滤器">
-            <Power HornorPower="300">
-                <Param Value="100"/>
-                <Param Value="200"/>
-            </Power>
-            <Power HornorPower="600">
-                <Param Value="130"/>
-                <Param Value="300"/>
-            </Power>
-            <Power HornorPower="1000">
-                <Param Value="150"/>
-                <Param Value="500"/>
-            </Power>
-            <Power HornorPower="99999">
-                <Param Value="200"/>
-                <Param Value="600"/>
-            </Power>
-        </Filter>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-
-
-    <MatchMode MissionType="MT_PVP_High_Rank_RoundBattle_1V1" desc="旧高段位排位赛">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="15s" /> <!-- MaxTime是最长匹配时间 ConfirmTime是等待确认时间 -->
-        <Open BeginDate="20200506" BeginTime="000000"  EndDate="20500506" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
+        <Open BeginDate="20200506" BeginTime="000000" EndDate="20500506" EndTime="235959" />
+        <Camp CampID="0" PlayerNum="1" desc="阵营1" />
+        <Camp CampID="1" PlayerNum="1" desc="阵营2" />
         <Strategy StrategyID="MatchStrategy_Glicko2" desc="匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxRatingGap" Open="true" UpdInterval="5s"  desc="过滤器"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-    <MatchMode MissionType="MT_PVP_Virtual_Tournament" desc="匹配模式  虚拟锦标赛">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="15s" /> <!-- MaxTime是最长匹配时间 ConfirmTime是等待确认时间 -->
-        <Open BeginDate="20200506" BeginTime="000000"  EndDate="20500506" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_Unify" desc="匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Route    Key="1" desc="路由哈希值" />
+        <Scope ScopeID="MatchScope_All" desc="范围" />
+        <Filter FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
+        <Filter FilterID="MatchFilter_MaxRatingGap" Open="true" UpdInterval="5s" desc="过滤器"/>
+        <Route Key="1" desc="路由哈希值" />
 
         <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
+            <AI Type="ENM_AI_NEWER" IsOpen="true" MinTime="2s" MaxTime="5s" desc="新手温暖局" />
+            <AI Type="ENM_AI_DAILY" IsOpen="true" MinTime="5s" MaxTime="15s" desc="每日温暖局" />
+            <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="30s" MaxTime="50s" desc="匹配超时AI" />
         </MatchAI>
     </MatchMode>
 
-    <MatchMode MissionType="MT_PVP_GroupBattle_2V2" desc="匹配模式 团体对战2v2">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20200506" BeginTime="000000"  EndDate="20500506" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="2" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="2" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_RoleHonorPower" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="0" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_SelectedRole" Open="true" Value="1"  desc="已选角色过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxHornorPowerGap" Open="true" UpdInterval="10s"  desc="荣耀战力匹配过滤器">
-            <Power HornorPower="300">
-                <Param Value="100"/>
-                <Param Value="200"/>
-            </Power>
-            <Power HornorPower="600">
-                <Param Value="130"/>
-                <Param Value="300"/>
-            </Power>
-            <Power HornorPower="1000">
-                <Param Value="150"/>
-                <Param Value="500"/>
-            </Power>
-            <Power HornorPower="99999">
-                <Param Value="200"/>
-                <Param Value="600"/>
-            </Power>
-        </Filter>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-    <MatchMode MissionType="MT_RMODE_DAILY" desc="R模式日(原心之试炼) 匹配队友">
-        <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="0s" />
-        <Open BeginDate="20200506" BeginTime="000000"  EndDate="20500506" EndTime="235959" />
-        <Strategy StrategyID="MatchStrategy_Unify" />
-        <Camp     CampID="0" PlayerNum="4" />
-        <Scope    ScopeID="MatchScope_All" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10" />
-        <Route    Key="2" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-    <MatchMode MissionType="MT_RMODE_MONTH" desc="R模式月(原狩魔秘境) 匹配队友">
-        <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="0s" />
-        <Open BeginDate="20200506" BeginTime="000000"  EndDate="20500506" EndTime="235959" />
-        <Strategy StrategyID="MatchStrategy_Unify" />
-        <Camp     CampID="0" PlayerNum="2" />
-        <Scope    ScopeID="MatchScope_All" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Value="5" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Value="10" />
-        <Route    Key="2" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-
-
-        <MatchMode MissionType="MT_CLASSIC_PVP_3v3" desc="旧 3v3 玩法">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="0s" />
-        <Open BeginDate="20201216" BeginTime="200000"  EndDate="20201220" EndTime="203000" />
-        <Camp     CampID="0" PlayerNum="3" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="3" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_Unify" desc="匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Route    Key="1" desc="路由哈希值" />
-
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-
-    <MatchMode MissionType="MT_BOUNTY_FUBEN" desc="旧 悬赏副本玩法" >
-        <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="0s" />
-        <Open BeginDate="20200506" BeginTime="000000"  EndDate="20500506" EndTime="235959" />
-        <Strategy StrategyID="MatchStrategy_Unify" />
-        <Camp     CampID="0" PlayerNum="3" />
-        <Scope    ScopeID="MatchScope_All" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10" />
-        <Route    Key="2" desc="路由哈希值" />
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-    <MatchMode MissionType="MT_CLASSIC_PVP_HeadCount" desc="旧-人头战玩法">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
-        <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="15s" />
-        <Open BeginDate="20201216" BeginTime="000000"  EndDate="20221220" EndTime="235959" />
-        <Camp     CampID="0" PlayerNum="2" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="2" desc="阵营2" />
-        <Strategy StrategyID="MatchStrategy_Unify" desc="匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Route    Key="1" desc="路由哈希值" />
-        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		      <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		      <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		      <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		      <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		      <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		      <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-        </MatchAI>
-    </MatchMode>
-
-
     <MatchMode MissionType="MT_CLASSIC_PVP_1V1" desc="旧经典1v1">
-        <!-- 匹配最短时间 匹配最长时间 每隔少秒匹配一次-->
         <Time MinTime="5s" MaxTime="90s" Freq="3" ConfirmTime="0s" />
-       <Open BeginDate="20201216" BeginTime="200000"  EndDate="20201220" EndTime="203000" />
-        <Camp     CampID="0" PlayerNum="1" desc="阵营1" />
-        <Camp     CampID="1" PlayerNum="1" desc="阵营2" />
+        <Open BeginDate="20201216" BeginTime="200000" EndDate="20201220" EndTime="203000" />
+        <Camp CampID="0" PlayerNum="1" desc="阵营1" />
+        <Camp CampID="1" PlayerNum="1" desc="阵营2" />
         <Strategy StrategyID="MatchStrategy_RoleHonorPower" desc="荣耀战力匹配策略" />
-        <Scope    ScopeID="MatchScope_All"  desc="范围" />
-        <Filter   FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
-        <Filter   FilterID="MatchFilter_MaxRoleNum" Open="true" Value="5" desc="过滤器" />
-        <Filter   FilterID="MatchFilter_MaxLevelGap" Open="true" Value="10"  desc="过滤器"/>
-        <Filter   FilterID="MatchFilter_MaxHornorPowerGap" Open="true" UpdInterval="10s"  desc="荣耀战力匹配过滤器">
+        <Scope ScopeID="MatchScope_All" desc="范围" />
+        <Filter FilterID="MatchFilter_Platform" Open="false" Value="1" desc="平台过滤器Open为1表示开启" />
+        <Filter FilterID="MatchFilter_MaxHornorPowerGap" Open="true" UpdInterval="10s" desc="荣耀战力匹配过滤器">
             <Power HornorPower="300">
                 <Param Value="100"/>
                 <Param Value="200"/>
@@ -778,30 +71,13 @@
                 <Param Value="130"/>
                 <Param Value="300"/>
             </Power>
-            <Power HornorPower="1000">
-                <Param Value="150"/>
-                <Param Value="500"/>
-            </Power>
-            <Power HornorPower="99999">
-                <Param Value="200"/>
-                <Param Value="600"/>
-            </Power>
         </Filter>
-        <Route    Key="1" desc="路由哈希值" />
-
-      <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
-		    <AI Type="ENM_AI_NEWER"   IsOpen="true" MinTime="2s" MaxTime="5s"  desc="新手温暖局" />
-		    <AI Type="ENM_AI_DAILY"   IsOpen="true" MinTime="5s" MaxTime="15s"  desc="每日温暖局" />
-		    <AI Type="ENM_AI_TIMEOUT" IsOpen="true" MinTime="20s" MaxTime="40s"  desc="匹配超时AI" />
-		    <AI Type="ENM_AI_CMD"     IsOpen="true" MinTime="2s" MaxTime="5s"  desc="CMD匹配AI" />
-		    <AI Type="ENM_AI_RETURN"  IsOpen="true" MinTime="5s" MaxTime="15s"  desc="回归玩家AI" />
-		    <AI Type="ENM_AI_TRAIN"   IsOpen="false" MinTime="5s" MaxTime="15s"  desc="训练玩家AI" />
-    </MatchAI>
+        <Route Key="1" desc="路由哈希值" />
 
+        <MatchAI IsOpen="true" PlayerOnlyOneCamp="false" GuaranteeAIRatio="0">
+            <AI Type="ENM_AI_NEWER" IsOpen="true" MinTime="2s" MaxTime="5s" desc="新手温暖局" />
+            <AI Type="ENM_AI_DAILY" IsOpen="true" MinTime="5s" MaxTime="15s" desc="每日温暖局" />
+            <AI Type="ENM_AI_TRAIN" IsOpen="false" MinTime="5s" MaxTime="15s" desc="训练玩家AI" />
+        </MatchAI>
     </MatchMode>
-
-
-
-
-
 </MatchCfg>
diff --git a/test/testdata/xml/match/SimpleMatchConf.xml b/test/testdata/xml/match/SimpleMatchConf.xml
new file mode 100644
index 0000000..7341a28
--- /dev/null
+++ b/test/testdata/xml/match/SimpleMatchConf.xml
@@ -0,0 +1,17 @@
+<@TABLEAU/>
+
+<SimpleMatchConf>
+    <!-- 匹配确认超时惩罚时间 -->
+    <ConfirmTimeOutPunish Time="2s" />
+    <MatchRoom MaxLoop="100" />
+
+    <TeamRatingWeight>
+        <Weight Num="1">
+            <Param Value="100"/>
+        </Weight>
+        <Weight Num="2">
+            <Param Value="30"/>
+            <Param Value="70"/>
+        </Weight>
+    </TeamRatingWeight>
+</SimpleMatchConf>