【特性】新增validator set, 要求值必须在某一集合内

【重构】重构validator,基于注解获得所有Validator类
main
walon 2021-11-22 15:50:05 +08:00
parent f024ec6974
commit 5e56d35e4a
26 changed files with 160 additions and 67 deletions

View File

@ -18,7 +18,7 @@
## 介绍 ## 介绍
在中大型游戏项目中excel配置表时常出现较复杂的数据结构常规的导表工具面对此类需求要么无法支持要么就强迫策划和程序使用拆表等奇技淫巧严重影响设计和开发效率。另外具有复杂GamePlay的游戏中技能、行为树、关卡之类的功能有非常复杂的数据结构往往使用自定义编辑器制作并以json、xml等文件格式保存以excel为中心的导表工具无法处理这些数据给策划和程序的工作流带来麻烦。 在中大型游戏项目中excel配置表时常出现较复杂的数据结构常规的导表工具面对此类需求要么无法支持要么就强迫策划和程序使用拆表等奇技淫巧严重影响设计和开发效率。另外具有复杂GamePlay的游戏中技能、行为树、关卡之类的功能有非常复杂的数据结构,它们往往使用自定义编辑器制作并以json、xml等文件格式保存以excel为中心的导表工具无法处理这些数据给策划和程序的工作流带来麻烦。
luban相较于常规的excel导表工具有以下核心优势 luban相较于常规的excel导表工具有以下核心优势
- 增强了excel格式。可以比较简洁地excel配置**任意复杂**的数据,像子结构、结构列表,以及更复杂的深层次的嵌套结构都能直接解析处理。 - 增强了excel格式。可以比较简洁地excel配置**任意复杂**的数据,像子结构、结构列表,以及更复杂的深层次的嵌套结构都能直接解析处理。
@ -30,7 +30,7 @@ luban相较于常规的excel导表工具有以下核心优势
## 文档 ## 文档
- [快速上手](docs/start_up.md) - [快速上手](docs/start_up.md)
- [wiki](https://github.com/focus-creative-games/luban/wiki) 有使用疑问请先查看此wiki。 - [**wiki**](https://github.com/focus-creative-games/luban/wiki) ,比较完善,有使用疑问请先查看此wiki。
- **示例项目** ([github](https://github.com/focus-creative-games/luban_examples)) ([gitee](https://gitee.com/focus-creative-games/luban_examples)) - **示例项目** ([github](https://github.com/focus-creative-games/luban_examples)) ([gitee](https://gitee.com/focus-creative-games/luban_examples))
- [版本变更记录](https://github.com/focus-creative-games/luban/wiki/%E7%89%88%E6%9C%AC%E5%8F%98%E6%9B%B4%E8%AE%B0%E5%BD%95) - [版本变更记录](https://github.com/focus-creative-games/luban/wiki/%E7%89%88%E6%9C%AC%E5%8F%98%E6%9B%B4%E8%AE%B0%E5%BD%95)

View File

@ -49,7 +49,7 @@ namespace Luban.Client.Utils
} }
} }
private readonly static List<string> _filterSuffixs = new List<string> private static readonly List<string> _filterSuffixs = new List<string>
{ {
".xlsx", ".xlsx",
".csv", ".csv",

View File

@ -30,7 +30,7 @@ namespace Luban.Job.Cfg.DataConverts
public override string Accept(DEnum type) public override string Accept(DEnum type)
{ {
return type.Value.ToString(); return $"'{type.StrValue}'";
} }
public override string Accept(DBean type) public override string Accept(DBean type)

View File

@ -91,8 +91,8 @@ namespace Luban.Job.Cfg.DataCreators
throw new NotSupportedException(); throw new NotSupportedException();
} }
private readonly static YamlScalarNode s_keyNodeName = new("key"); private static readonly YamlScalarNode s_keyNodeName = new("key");
private readonly static YamlScalarNode s_textNodeName = new("text"); private static readonly YamlScalarNode s_textNodeName = new("text");
public DType Accept(TText type, YamlNode x, DefAssembly y) public DType Accept(TText type, YamlNode x, DefAssembly y)
{ {
@ -102,7 +102,7 @@ namespace Luban.Job.Cfg.DataCreators
return new DText(key, text); return new DText(key, text);
} }
private readonly static YamlScalarNode s_typeNodeName = new(DefBean.TYPE_NAME_KEY); private static readonly YamlScalarNode s_typeNodeName = new(DefBean.TYPE_NAME_KEY);
public DType Accept(TBean type, YamlNode x, DefAssembly y) public DType Accept(TBean type, YamlNode x, DefAssembly y)
{ {
@ -204,10 +204,10 @@ namespace Luban.Job.Cfg.DataCreators
return new DMap(type, map); return new DMap(type, map);
} }
private readonly static YamlScalarNode s_xNodeName = new("x"); private static readonly YamlScalarNode s_xNodeName = new("x");
private readonly static YamlScalarNode s_yNodeName = new("y"); private static readonly YamlScalarNode s_yNodeName = new("y");
private readonly static YamlScalarNode s_zNodeName = new("z"); private static readonly YamlScalarNode s_zNodeName = new("z");
private readonly static YamlScalarNode s_wNodeName = new("w"); private static readonly YamlScalarNode s_wNodeName = new("w");
private static float ParseChildFloatValue(YamlNode node, YamlScalarNode keyName) private static float ParseChildFloatValue(YamlNode node, YamlScalarNode keyName)
{ {

View File

@ -10,7 +10,7 @@ namespace Luban.Job.Cfg.DataExporters
{ {
class Json2Exportor : JsonExportor class Json2Exportor : JsonExportor
{ {
public new static Json2Exportor Ins { get; } = new(); public static new Json2Exportor Ins { get; } = new();
public void WriteAsObject(DefTable table, List<Record> datas, Utf8JsonWriter x) public void WriteAsObject(DefTable table, List<Record> datas, Utf8JsonWriter x)
{ {

View File

@ -51,7 +51,7 @@ namespace Luban.Job.Cfg.DataSources.Yaml
return records; return records;
} }
private readonly static YamlScalarNode s_tagNameNode = new(TAG_KEY); private static readonly YamlScalarNode s_tagNameNode = new(TAG_KEY);
public override Record ReadOne(TBean type) public override Record ReadOne(TBean type)
{ {

View File

@ -0,0 +1,23 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Utils;
using System.Collections.Generic;
using System.Text;
namespace Luban.Job.Cfg.DataVisitors
{
class ToStringVisitor2 : ToStringVisitor
{
public static new ToStringVisitor2 Ins { get; } = new();
public override string Accept(DEnum type)
{
return type.Value.ToString();
}
public override string Accept(DString type)
{
return DataUtil.EscapeString(type.Value);
}
}
}

View File

@ -49,7 +49,7 @@ namespace Luban.Job.Cfg.DataVisitors
} }
protected override void Append(List<DType> datas, StringBuilder x) override protected void Append(List<DType> datas, StringBuilder x)
{ {
x.Append('{'); x.Append('{');
foreach (var e in datas) foreach (var e in datas)

View File

@ -2,6 +2,7 @@ using Luban.Common.Utils;
using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources; using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.Defs; using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Utils;
using Luban.Job.Cfg.Validators; using Luban.Job.Cfg.Validators;
using Luban.Job.Common.Types; using Luban.Job.Common.Types;
using Luban.Job.Common.TypeVisitors; using Luban.Job.Common.TypeVisitors;
@ -12,7 +13,6 @@ namespace Luban.Job.Cfg.DataVisitors
{ {
public class ValidatorVisitor : TypeActionVisitorAdaptor<DType> public class ValidatorVisitor : TypeActionVisitorAdaptor<DType>
{ {
public const string TAG_UNCHECKED = "unchecked";
private readonly Stack<object> _path = new Stack<object>(); private readonly Stack<object> _path = new Stack<object>();
@ -33,7 +33,7 @@ namespace Luban.Job.Cfg.DataVisitors
foreach (Record r in records) foreach (Record r in records)
{ {
if (r.Tags != null && r.Tags.Count > 0 && r.Tags.Contains(TAG_UNCHECKED)) if (DataUtil.IsUnchecked(r))
{ {
continue; continue;
} }

View File

@ -41,6 +41,11 @@ namespace Luban.Job.Cfg.Datas
return visitor.Accept(this, x); return visitor.Accept(this, x);
} }
public override string ToString()
{
return $"{StrValue}({Value})";
}
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
switch (obj) switch (obj)

View File

@ -661,7 +661,7 @@ namespace Luban.Job.Cfg.Defs
private static readonly List<string> _fieldRequireAttrs = new List<string> { "name", "type" }; private static readonly List<string> _fieldRequireAttrs = new List<string> { "name", "type" };
protected override Field CreateField(string defineFile, XElement e) override protected Field CreateField(string defineFile, XElement e)
{ {
ValidAttrKeys(defineFile, e, _fieldOptionalAttrs, _fieldRequireAttrs); ValidAttrKeys(defineFile, e, _fieldOptionalAttrs, _fieldRequireAttrs);
@ -726,7 +726,7 @@ namespace Luban.Job.Cfg.Defs
private static readonly List<string> _beanOptinsAttrs = new List<string> { "value_type", "alias", "sep", "comment", "tags" }; private static readonly List<string> _beanOptinsAttrs = new List<string> { "value_type", "alias", "sep", "comment", "tags" };
private static readonly List<string> _beanRequireAttrs = new List<string> { "name" }; private static readonly List<string> _beanRequireAttrs = new List<string> { "name" };
protected override void AddBean(string defineFile, XElement e, string parent) override protected void AddBean(string defineFile, XElement e, string parent)
{ {
ValidAttrKeys(defineFile, e, _beanOptinsAttrs, _beanRequireAttrs); ValidAttrKeys(defineFile, e, _beanOptinsAttrs, _beanRequireAttrs);

View File

@ -40,7 +40,7 @@ namespace Luban.Job.Cfg.Defs
{ {
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
public new static DefAssembly LocalAssebmly { get => (DefAssembly)DefAssemblyBase.LocalAssebmly; set => DefAssemblyBase.LocalAssebmly = value; } public static new DefAssembly LocalAssebmly { get => (DefAssembly)DefAssemblyBase.LocalAssebmly; set => DefAssemblyBase.LocalAssebmly = value; }
public Service CfgTargetService { get; private set; } public Service CfgTargetService { get; private set; }

View File

@ -103,7 +103,7 @@ namespace Luban.Job.Cfg.Defs
Sep = b.Sep; Sep = b.Sep;
} }
protected override DefFieldBase CreateField(Common.RawDefs.Field f, int idOffset) override protected DefFieldBase CreateField(Common.RawDefs.Field f, int idOffset)
{ {
return new DefField(this, (CfgField)f, idOffset); return new DefField(this, (CfgField)f, idOffset);
} }

View File

@ -1,5 +1,6 @@
using Luban.Common.Protos; using Luban.Common.Protos;
using Luban.Job.Cfg.Cache; using Luban.Job.Cfg.Cache;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Utils; using Luban.Job.Cfg.Utils;
using Luban.Job.Common.Utils; using Luban.Job.Common.Utils;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -21,7 +22,7 @@ namespace Luban.Job.Cfg.Generate
foreach (var record in records) foreach (var record in records)
{ {
var fileName = table.IsMapTable ? var fileName = table.IsMapTable ?
record.Data.GetField(table.IndexField.Name).ToString().Replace("\"", "").Replace("'", "") record.Data.GetField(table.IndexField.Name).Apply(ToStringVisitor2.Ins).Replace("\"", "").Replace("'", "")
: (++index).ToString(); : (++index).ToString();
var file = RenderFileUtil.GetOutputFileName(genType, $"{dirName}/{fileName}", ctx.GenArgs.DataFileExtension); var file = RenderFileUtil.GetOutputFileName(genType, $"{dirName}/{fileName}", ctx.GenArgs.DataFileExtension);
ctx.Tasks.Add(Task.Run(() => ctx.Tasks.Add(Task.Run(() =>

View File

@ -1,5 +1,6 @@
using Luban.Common.Utils; using Luban.Common.Utils;
using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources;
using Luban.Job.Cfg.Defs; using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.TypeVisitors; using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Common.Types; using Luban.Job.Common.Types;
@ -137,6 +138,14 @@ namespace Luban.Job.Cfg.Utils
return tags.Count > 0 ? tags : null; return tags.Count > 0 ? tags : null;
} }
private const string TAG_UNCHECKED = "unchecked";
public static bool IsUnchecked(Record rec)
{
return rec.Tags != null && rec.Tags.Count > 0 && rec.Tags.Contains(TAG_UNCHECKED);
}
public const string SimpleContainerSep = ",;|"; public const string SimpleContainerSep = ",;|";
public static string GetBeanSep(TBean type) public static string GetBeanSep(TBean type)

View File

@ -151,10 +151,9 @@ namespace Luban.Job.Cfg.Validators
} }
} }
[Validator("path")]
public class PathValidator : IValidator public class PathValidator : IValidator
{ {
public const string NAME = "path";
public string RawPattern { get; } public string RawPattern { get; }
public TType Type { get; } public TType Type { get; }

View File

@ -6,12 +6,9 @@ using System;
namespace Luban.Job.Cfg.Validators namespace Luban.Job.Cfg.Validators
{ {
[Validator("range")]
class RangeValidator : IValidator class RangeValidator : IValidator
{ {
public const string NAME = "range";
public string Name => NAME;
public TType Type { get; } public TType Type { get; }
private readonly string _str; private readonly string _str;

View File

@ -9,6 +9,7 @@ using System.Linq;
namespace Luban.Job.Cfg.Validators namespace Luban.Job.Cfg.Validators
{ {
[Validator("ref")]
public class RefValidator : IValidator public class RefValidator : IValidator
{ {
@ -21,18 +22,16 @@ namespace Luban.Job.Cfg.Validators
#endif #endif
} }
public const string NAME = "ref";
public List<string> Tables { get; } public List<string> Tables { get; }
public string FirstTable => GetActualTableName(Tables[0]); public string FirstTable => GetActualTableName(Tables[0]);
public TType Type { get; } public TType Type { get; }
public RefValidator(TType type, List<string> tables) public RefValidator(TType type, string tablesStr)
{ {
Type = type; Type = type;
this.Tables = new List<string>(tables); this.Tables = new List<string>(tablesStr.Split(','));
} }
public void Validate(ValidatorContext ctx, TType type, DType key) public void Validate(ValidatorContext ctx, TType type, DType key)

View File

@ -0,0 +1,50 @@
using Luban.Job.Cfg.DataCreators;
using Luban.Job.Cfg.Datas;
using Luban.Job.Common.Defs;
using Luban.Job.Common.Types;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.Validators
{
[Validator("set")]
class SetValidator : IValidator
{
private readonly TType _type;
private readonly HashSet<DType> _datas;
private readonly string _valueSetStr;
public SetValidator(TType ttype, string param)
{
_type = ttype;
_datas = new HashSet<DType>();
_valueSetStr = param;
}
public void Compile(DefFieldBase def)
{
foreach (var p in _valueSetStr.Split(',', ';'))
{
_datas.Add(_type.Apply(StringDataCreator.Ins, p));
}
}
public void Validate(ValidatorContext ctx, TType type, DType data)
{
if (type.IsNullable && data == null)
{
return;
}
if (!_datas.Contains(data))
{
ctx.Assembly.Agent.Error("记录 {0}:{1} (来自文件:{2}) 值不在set:{3}中", ValidatorContext.CurrentRecordPath, data,
ValidatorContext.CurrentVisitor.CurrentValidateRecord.Source, _valueSetStr);
}
}
}
}

View File

@ -9,15 +9,13 @@ using System.Threading.Tasks;
namespace Luban.Job.Cfg.Validators namespace Luban.Job.Cfg.Validators
{ {
[Validator("size")]
internal class SizeValidator : IValidator internal class SizeValidator : IValidator
{ {
public const string NAME = "size";
private readonly TType _field;
private readonly int _size; private readonly int _size;
public SizeValidator(TType field, string rule) public SizeValidator(TType type, string rule)
{ {
this._field = field;
this._size = int.Parse(rule); this._size = int.Parse(rule);
} }
@ -26,7 +24,7 @@ namespace Luban.Job.Cfg.Validators
} }
private string Source => ValidatorContext.CurrentVisitor.CurrentValidateRecord.Source; private static string Source => ValidatorContext.CurrentVisitor.CurrentValidateRecord.Source;
public void Validate(ValidatorContext ctx, TType type, DType data) public void Validate(ValidatorContext ctx, TType type, DType data)
{ {

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.Validators
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
internal class ValidatorAttribute : Attribute
{
public string Name { get; }
public ValidatorAttribute(string name)
{
Name = name;
}
}
}

View File

@ -11,38 +11,31 @@ namespace Luban.Job.Cfg.Validators
{ {
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
private readonly static List<string> s_validatorNames = new List<string>() static ValidatorFactory()
{ {
RefValidator.NAME, foreach (var type in Bright.Common.ReflectionUtil.GetCallingTypesByAttr(typeof(ValidatorAttribute)))
PathValidator.NAME, {
RangeValidator.NAME, var va = (ValidatorAttribute)type.GetCustomAttributes(typeof(ValidatorAttribute), false)[0];
SizeValidator.NAME s_validators.Add(va.Name, type);
}; s_validatorNames.Add(va.Name);
}
}
private static readonly Dictionary<string, Type> s_validators = new Dictionary<string, Type>();
private static readonly List<string> s_validatorNames = new List<string>();
public static List<string> ValidatorNames => s_validatorNames; public static List<string> ValidatorNames => s_validatorNames;
public static IValidator Create(TType field, string type, string rule) public static IValidator Create(TType ttype, string type, string rule)
{ {
s_logger.Debug("== create validator {type}:{rule}", type, rule); s_logger.Debug("== create validator {type}:{rule}", type, rule);
switch (type) if (s_validators.TryGetValue(type, out var validatorType))
{
return (IValidator)System.Activator.CreateInstance(validatorType, ttype, rule);
}
else
{ {
case RefValidator.NAME:
{
return new RefValidator(field, rule.Split(',').ToList());
}
case PathValidator.NAME:
{
return new PathValidator(field, rule);//.Split(',').ToList());
}
case RangeValidator.NAME:
{
return new RangeValidator(field, rule);
}
case SizeValidator.NAME:
{
return new SizeValidator(field, rule);
}
default:
throw new NotSupportedException("unknown validator type:" + type); throw new NotSupportedException("unknown validator type:" + type);
} }
} }

View File

@ -4,7 +4,7 @@ namespace Luban.Job.Common.TypeVisitors
{ {
public class CppSharedPtrUnderingDefineTypeName : CppRawUnderingDefineTypeName public class CppSharedPtrUnderingDefineTypeName : CppRawUnderingDefineTypeName
{ {
public new static CppSharedPtrUnderingDefineTypeName Ins { get; } = new(); public static new CppSharedPtrUnderingDefineTypeName Ins { get; } = new();
public override string Accept(TBean type) public override string Accept(TBean type)
{ {

View File

@ -8,9 +8,9 @@ namespace Luban.Job.Common.Utils
{ {
public class DefUtil public class DefUtil
{ {
private readonly static char[] s_attrSep = new char[] { '|', '#', '&' }; private static readonly char[] s_attrSep = new char[] { '|', '#', '&' };
private readonly static char[] s_attrKeyValueSep = new char[] { '=', ':' }; private static readonly char[] s_attrKeyValueSep = new char[] { '=', ':' };
public static Dictionary<string, string> ParseAttrs(string tags) public static Dictionary<string, string> ParseAttrs(string tags)
{ {

View File

@ -59,7 +59,7 @@ namespace Luban.Job.Common.Utils
return fullName + ".bin"; return fullName + ".bin";
} }
private readonly static Dictionary<string, ELanguage> s_name2Lans = new() private static readonly Dictionary<string, ELanguage> s_name2Lans = new()
{ {
{ "cs", ELanguage.CS }, { "cs", ELanguage.CS },
{ "java", ELanguage.JAVA }, { "java", ELanguage.JAVA },
@ -85,7 +85,7 @@ namespace Luban.Job.Common.Utils
throw new ArgumentException($"not support output data type:{genType}"); throw new ArgumentException($"not support output data type:{genType}");
} }
private readonly static Dictionary<string, string> s_name2Suxxifx = new() private static readonly Dictionary<string, string> s_name2Suxxifx = new()
{ {
{ "json", "json" }, { "json", "json" },
{ "lua", "lua" }, { "lua", "lua" },

View File

@ -6,7 +6,7 @@ namespace Luban.Job.Db.TypeVisitors
{ {
class DbTypescriptDefineTypeNameVisitor : TypescriptDefineTypeNameVisitor class DbTypescriptDefineTypeNameVisitor : TypescriptDefineTypeNameVisitor
{ {
public new static DbTypescriptDefineTypeNameVisitor Ins { get; } = new(); public static new DbTypescriptDefineTypeNameVisitor Ins { get; } = new();
public override string Accept(TArray type) public override string Accept(TArray type)
{ {