{% macro decl_units(units) %} {%- for u in units ~%} {%- if u.object is instanceof('\\mtgMetaStruct') -%} {{ _self.decl_struct(u.object) }} {%- elseif u.object is instanceof('\\mtgMetaEnum') -%} {{ _self.decl_enum(u.object) }} {%- elseif u.object is instanceof('\\mtgMetaRPC') -%} {{ _self.decl_rpc(u.object) }} {%- endif ~%} {%- endfor ~%} {%- for ai in get_all_declarable_accessor_interfaces(meta) ~%} {{ _self.decl_accessor_interface(ai) }} {%- endfor ~%} {% endmacro %} {% macro decl_struct(o, extra = '') %} {% set pkey_fields = token(o, 'table_pkey')|split(',') %} {% set pkey = pkey_fields|filter(f => f|length > 0)|map(f => o.getField(f)) %} {%- if o.parent and has_token(o, 'POD') -%} {{Error("@POD structs can't have a parent: " ~ o.name)}} {%- endif -%} {%- if o.parent and has_token(o, 'bitfields') -%} {{Error("@bitfields structs can't have a parent: " ~ o.name)}} {%- endif -%} {{_self.attributes(o)}} public {{_self.struct_type(o)}} {{o.name}} {{_self.base_struct_class(o)}} { {{token(o, 'cs_embed', '')}} {{_self.decl_struct_fields(o)}} public {{o.parent ? 'new'}} const uint STATIC_CLASS_ID = {{o.classid}}; public {{_self.override(o)}} uint CLASS_ID() { return {{o.classid}}; } {{_self.comment_POD_begin(o)}} public {{o.name}}() { Reset(); } {{_self.comment_POD_end(o)}} public {{_self.override(o)}} void Reset() { {{_self.struct_fields_reset(o)}} } {{_self.comment_non_cloneable_begin(o)}} public void CloneTo(ref {{o.name}} dst) { MetaIO.Clone(this, ref dst, AutogenBundle.createById); } public {{_self.virtual_clone(o)}} IMetaStruct Clone() { var dst = new {{o.name}}(); CloneTo(ref dst); return dst; } {{_self.comment_non_cloneable_end(o)}} public {{_self.override(o)}} void SyncFields(MetaSyncContext ctx) { {{_self.sync_fields(o)}} } public {{_self.override(o)}} int GetFieldsCount() { return {{fields_count(o)}}; } public {{_self.override(o)}} int GetDirtyFieldsCount() { {%~ if has_token(o, 'bitfields') ~%} return fields_mask.GetDirtyFieldsCount(); {% else %} return GetFieldsCount(); {%- endif ~%} } {%- if has_token(o, 'bitfields') ~%} {{_self.bitmask_helpers(o)}} {%- endif -%} {%- if has_token(o, 'table') ~%} public static bool operator <({{o.name}} o1, {{o.name}} o2) { return {{o.name}}Comparer.self.Compare(o1, o2) < 0; } public static bool operator >({{o.name}} o1, {{o.name}} o2) { return {{o.name}}Comparer.self.Compare(o1, o2) > 0; } public {{o.name}}Id MakeId() { return new {{o.name}}Id{ {% for f in pkey %} {{f.name}} = {{f.name}}, {% endfor %} }; } {%- endif -%} {%- if has_token(o, 'diffable') ~%} {{_self.diffable_support(o)}} {%- endif -%} {{_self.decl_struct_accessor_implements(o)}} {{extra}} } {%- if has_token(o, 'table') ~%} public class {{o.name}}Comparer : IComparer<{{o.name}}> { public readonly static {{o.name}}Comparer self = new {{o.name}}Comparer(); public int Compare({{o.name}} o1, {{o.name}} o2) { int result; {% set comparable = pkey %} {% if comparable|length > 1 %} {% set comparable = comparable [1:] %} {% endif %} {% for f in comparable %} {% if not loop.first %} if(result == 0) {% endif %} result = o1.{{f.name}}.CompareTo(o2.{{f.name}}); {% endfor %} return result; } } {%- endif ~%} {% endmacro %} {%- macro decl_struct_fields(o) -%} {%- if has_token(o, 'bitfields') ~%} public FieldsMask fields_mask; {%- endif -%} {%- for f in o.fields ~%} {{_self.decl_struct_field(o, f)}} {%- endfor -%} {%- endmacro -%} {%- macro decl_struct_field(o, f) -%} {{_self.attributes(f)}} {% if has_token(f, 'flt_i18n') -%} {{f.type}} _{{f.name}} = ""; List<{{f.type}}> __{{f.name}} = new List<{{f.type}}>(); {%- endif ~%} public {{f.type|cs_type|obscure_type(f)}} {{f.name}} {% if not has_token(o, 'POD') -%} {{_self.decl_init_value(f)}} {%- endif -%} {% if not has_token(f, 'flt_i18n') -%};{%- endif -%} {%- endmacro -%} {% macro decl_init_value(f) %} {%- if f.type is instanceof('\\mtgBuiltinType') -%} {%- if f.type.isstring -%} {% if has_token(f, 'flt_i18n') -%} {get{return plural_{{f.name}}(double.NaN);}set{_{{f.name}}=value;}} {{f.type}} plural_{{f.name}}(double force_n){return MetaI18N.I18NPick(_{{f.name}},__{{f.name}},"#",force_n);} {%- else -%} = "" {%- endif ~%} {%- endif -%} {%- else -%} {%- if has_token(f, 'default') and token(f, 'default') == 'null' -%} /*null*/ {%- else -%} = new {{f.type|cs_type}}() {%- endif -%} {%- endif -%} {% endmacro %} {% macro struct_fields_reset(o) %} {%- if o.parent ~%} base.Reset(); {%- endif -%} {%- for f in o.fields ~%} {% set fname = f.name %} {% if has_token(f, 'flt_i18n') -%} {% set fname = '_' ~ f.name %} if(_{{fname}} == null) _{{fname}} = new(); else _{{fname}}.Clear(); {%- endif ~%} {{var_reset(fname, f.type, token_or(f, 'default', null))}} {%- endfor -%} {%- if has_token(o, 'bitfields') ~%} ResetFieldMask(); {%- endif -%} {% endmacro %} {%- macro attributes(o) %} {% if has_token(o, "cs_obsolete") %} /// /// {{token(o, "cs_obsolete")}} /// [Obsolete] {% endif %} {%- if has_token(o, 'cs_attributes') -%} {%- for attr in token(o, 'cs_attributes')|split(',') -%} [{{attr}}] {%- endfor -%} {%- endif -%} {%- endmacro %} {%- macro struct_type(o) -%} {{has_token(o, 'POD') ? 'struct' : 'class'}} {%- endmacro -%} {%- macro base_struct_class(o) -%} {%- if has_token(o, 'POD') -%} : IMetaStruct {%- else -%} : {{o.parent ? o.parent.name : 'BaseMetaStruct'}} {{- has_token(o, 'cloneable') and not o.parent ? ', IMetaCloneable' -}} {%- endif -%} {%- if has_token(o, "cs_implements") -%} {%- for ifs in token(o, "cs_implements")|split(',') -%} , {{ifs}} {%- endfor -%} {%- endif -%} {%- for ai in get_accessor_interfaces(o) -%} , {{ai.interfacename}} {%- endfor -%} {%- endmacro -%} {%- macro override(o) -%} {%- if not has_token(o, 'POD') -%} override {%- endif -%} {%- endmacro -%} {%- macro virtual(o) -%} {%- if not has_token(o, 'POD') -%} {{o.parent ? 'override' : 'virtual'}} {%- endif -%} {%- endmacro -%} {%- macro comment_POD_begin(o) -%} {%- if has_token(o, 'POD') -%} /* commented in POD {%- endif -%} {%- endmacro -%} {%- macro comment_POD_end(o) -%} {%- if has_token(o, 'POD') -%} */ {%- endif -%} {%- endmacro -%} {%- macro comment_non_cloneable_begin(o) -%} {%- if not has_token(o, 'cloneable') -%} /* commented in non-cloneable {%- endif -%} {%- endmacro -%} {%- macro comment_non_cloneable_end(o) -%} {%- if not has_token(o, 'cloneable') -%} */ {%- endif -%} {%- endmacro -%} {%- macro virtual_clone(o) -%} {%- if has_token(o, 'cloneable') -%} {%- if has_token(o, 'POD') -%} {%- elseif o.parent and has_token_in_parent(o.parent, 'cloneable') -%} override {%- else -%} virtual {%- endif -%} {%- endif -%} {%- endmacro -%} {%- macro sync_fields(o) -%} {%- if has_token(o, 'bitfields') ~%} var bitctx = new MetaIO.BitfieldsContext(ctx, fields_mask); bitctx.SyncMaskHeader(); {%- endif -%} {%- if o.parent ~%} base.SyncFields(ctx); {%- endif -%} {%- for f in o.fields ~%} {% set fname = f.name %} {% if has_token(f, 'flt_i18n') -%} {% set fname = '_' ~ f.name %} {%- endif -%} {{var_sync(fname, f.type, 'ctx', f.tokens, get_sync_opts(o, 'bitctx'))}} {%- endfor -%} {%- endmacro -%} {%- macro bitmask_helpers(o) ~%} public void ResetFieldMask() { fields_mask = FieldsMask.MakeClean(fields_amount: {{fields_count(o)}}); } public void SetPrimaryFieldsChanged() { SetPrimaryFieldsChanged(ref this.fields_mask); } static void SetPrimaryFieldsChanged(ref FieldsMask mask) { {%- for f in o.fields ~%} {%- if is_primary_field(o, f) ~%} MetaIO.SetFieldDirty(ref mask, {{loop.index0}}); {%- endif -%} {%- endfor ~%} } public bool IsContentDirty() { int fields_amount = {{fields_count(o)}}; var primary_id_mask = FieldsMask.MakeClean(fields_amount); SetPrimaryFieldsChanged(ref primary_id_mask); for(int i = 0; i < fields_amount; i++) { bool is_primary_id = primary_id_mask.IsDirty(i); if(!is_primary_id && fields_mask.IsDirty(i)) return true; } return false; } public void SetDirtyMask() { fields_mask = FieldsMask.MakeDirty(fields_amount: {{fields_count(o)}}); } public void SetDirtyMaskDeep() { SetDirtyMask(); {%- for f in o.fields ~%} {%- if f.type is instanceof('\\mtgMetaStruct') and has_token(f.type, 'bitfields') ~%} {{f.name}}.SetDirtyMaskDeep(); {%- elseif f.type is instanceof('\\mtgArrType') and f.type.value is instanceof('\\mtgMetaStruct') and has_token(f.type.value, "bitfields") ~%} { for(int i=0;i<{{f.name}}.Count;++i) { var __tmp = {{f.name}}[i]; __tmp.SetDirtyMaskDeep(); {{f.name}}[i] = __tmp; } } {%- endif -%} {%- endfor ~%} } {%- endmacro -%} {% macro decl_enum(o) %} {{_self.attributes(o)}} public enum {{o.name}} { {%- for n,v in o.values ~%} {{n}} = {{v}}, {%- endfor ~%} } {% endmacro %} {% macro decl_rpc(o, is_global = true) %} {% if is_global %} {{_self.decl_struct(o.req)}} {{_self.decl_struct(o.rsp)}} {% endif %} public class {{o.name}} : IRpc { {% if not is_global %} {{_self.decl_struct(o.req)}} {{_self.decl_struct(o.rsp)}} {% endif %} public IRpcError error = null; public {{o.req.name}} req = new {{o.req.name}}(); public {{o.rsp.name}} rsp = new {{o.rsp.name}}(); public int GetCode() { return {{o.code}}; } public void SetError(IRpcError error) { this.error = error; } public IRpcError GetError() { return error; } public IMetaStruct GetRequest() { return req as IMetaStruct; } public IMetaStruct GetResponse() { return rsp as IMetaStruct; } } {% endmacro %} {%- macro diffable_support(o) -%} public bool GetDiff({{o.name}} old, {{o.name}} diff = null, {{o.name}}RemovedIds removed = null) { if(diff != null) diff.Reset(); if(removed != null) removed.Reset(); bool no_changes = true; {% set field_idx = -1 %} {%- for f in o.fields ~%} {% set field_idx = field_idx + 1 %} {%- if not has_token(f, 'nodiff') -%} {{_self.field_diff(f, field_idx, o)}} {%- endif -%} {% endfor %} return !no_changes; } {% set unique_diff_units = [] %} {% for u in get_diff_related_units(o) %} {% if u not in unique_diff_units %} {{_self.diff_methods_for(u)}} {% set unique_diff_units = unique_diff_units|merge([u]) %} {% endif %} {% endfor %} {% endmacro %} {%- macro diff_methods_for(u) -%} {% if has_token(u, 'table') %} static public bool DiffOne(ref {{u.name}} curr, {{u.name}} old) { return !{{_self.compare_func_name(u)}}(ref curr, old); } static public bool DiffCollection(List<{{u.name}}> items, List<{{u.name}}> old_items, List<{{u.name}}> changed, List<{{u.name}}Id> removed) { bool has_diff = false; if(changed != null) changed.Clear(); items.Sort({{u.name}}Comparer.self); old_items.Sort({{u.name}}Comparer.self); int i = 0; int old_i = 0; for(; i < items.Count && old_i < old_items.Count;) { var old = old_items[old_i]; var item = items[i]; if(old < item) { if(removed != null) removed.Add(old.MakeId()); old_i++; has_diff = true; } else if(old > item) { item.SetDirtyMaskDeep(); if(changed != null) changed.Add(item); i++; has_diff = true; } else { if(!{{_self.compare_func_name(u)}}(ref item, old) && item.IsContentDirty()) { if(changed != null) changed.Add(item); has_diff = true; } i++; old_i++; } } //items leftovers are considered changed for(; i < items.Count; i++) { var item = items[i]; item.SetDirtyMaskDeep(); if(changed != null) changed.Add(item); has_diff = true; } //old items leftovers are considered removed for(; old_i < old_items.Count; old_i++) { if(removed != null) removed.Add(old_items[old_i].MakeId()); has_diff = true; } //if(changed.Count > 0) // MetaIO.LogDebug("Changed in collection {{u.name}}"); //if(removed_ids.Count > 0) // MetaIO.LogDebug("Removed in collection {{u.name}}"); return has_diff; } {% endif %} public static bool {{_self.compare_func_name(u)}}(ref {{u.name}} a, {{u.name}} b) { bool is_equal = true; {%- if has_token(u, 'bitfields') ~%} a.ResetFieldMask(); {%- endif -%} {%- for f in u.fields ~%} {{_self.field_compare(u, f, loop.index0)}} {%- endfor -%} {%- if has_token(u, 'bitfields') ~%} if(!is_equal) a.SetPrimaryFieldsChanged(); {%- endif -%} return is_equal; } {% endmacro %} {% macro compare_func_name(o) %} {{has_token(o, 'bitfields')?'CompareAndMarkFields':'IsEqual'}} {%- endmacro %} {% macro field_compare_array_bitfields_nested_diffable(o, f, field_idx) %} { int {{f.name}}_count_a = a.{{f.name}} == null ? 0 : a.{{f.name}}.Count; int {{f.name}}_count_b = b.{{f.name}} == null ? 0 : b.{{f.name}}.Count; //For NESTED arrays of DIFFABLE structs: //for DB compatibility we must add the WHOLE ARRAY to the diff if ANY ELEMENT of the array has changed. //That means marking each element's fields_mask as dirty //Changed size means the array has changed, no furhter checks required. bool {{f.name}}_equal = {{f.name}}_count_a == {{f.name}}_count_b; //For same size arrays - check contents for changes. if({{f.name}}_equal) { for(int i=0;i<{{f.name}}_count_a && i<{{f.name}}_count_b;++i) { var tmp_{{f.name}} = a.{{f.name}}[i]; if(!CompareAndMarkFields(ref tmp_{{f.name}}, b.{{f.name}}[i])) { {{f.name}}_equal = false; break; } } } //If change detected - mark top-level field as changed and add the whole array to the diff if(!{{f.name}}_equal) { MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}}); for(int i=0; i<{{f.name}}_count_a;++i) { var tmp_{{f.name}} = a.{{f.name}}[i]; tmp_{{f.name}}.SetDirtyMaskDeep(); a.{{f.name}}[i] = tmp_{{f.name}}; is_equal = false; } } } {%- endmacro %} {% macro field_compare_array_bitfields_nested_plain(o, f, field_idx) %} if(a.{{f.name}} == null || b.{{f.name}} == null || a.{{f.name}}.Count != b.{{f.name}}.Count) { MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}}); is_equal = false; } else { for(int i=0;i 0) MetaIO.SetFieldDirty(ref removed.fields_mask, {{ field_index(find_struct(o.name ~ 'RemovedIds'), f.name) }}); } {% elseif f.type is instanceof('\\mtgBuiltinType') %} if(diff != null) diff.{{f.name}} = {{f.name}}; if({{f.name}} != old.{{f.name}}) { no_changes &= false; if(diff != null) MetaIO.SetFieldDirty(ref diff.fields_mask, {{field_idx}}); } {% else %} {{Error("Diff for field '"~ f.name ~"' is not supported") }} {% endif %} {% endmacro %} {% macro decl_accessor_interface(ai) ~%} public interface {{ai.interfacename}} { {%- for aif in ai.fields ~%} {% if aif.is_accessor -%} {{aif.field.type|cs_type}} Get{{aif.camelfieldname}}(); void Set{{aif.camelfieldname}}({{aif.field.type|cs_type}} v); {% endif -%} {%- if aif.is_propget -%} {{aif.field.type|cs_type}} {{aif.camelfieldname}} { {%- if aif.is_propget -%} get; {%- endif %} {%- if aif.is_propset -%} set; {%- endif -%} } {%- endif ~%} {%- endfor ~%} } {% endmacro %} {%- macro decl_struct_accessor_implements(o) -%} {%- for ai in get_accessor_interfaces(o) ~%} {%- for aif in ai.fields ~%} {% if aif.is_accessor %} public {{aif.field.type|cs_type}} Get{{aif.camelfieldname}}() { return {{aif.field.name}}; } public void Set{{aif.camelfieldname}}({{aif.field.type|cs_type}} v) { this.{{aif.field.name}} = v; } {% endif %} {% if aif.is_propget or aif.is_propset %} public {{aif.field.type|cs_type}} {{aif.camelfieldname}} { {%- if aif.is_propget -%} get => this.{{aif.field.name}}; {%- endif -%} {%- if aif.is_propset -%} set => this.{{aif.field.name}} = value; {%- endif -%} } {%- endif ~%} {% endfor %} {%- endfor ~%} {% endmacro %}