Compare commits

...

43 Commits

Author SHA1 Message Date
n.pankin 6146771ce7 Обновить CHANGELOG.md 2024-08-12 12:10:44 +03:00
n.pankin 8a1fb3126e added implementation of flt_i18n getter setter generation
Publish PHP Package / docker (push) Successful in 6s Details
2024-08-12 12:05:17 +03:00
madpwnhammer 06a96bfdca Added: cs_obsolete changelog 2024-08-08 12:10:43 +03:00
Madpwnhammer 6630a1a3ee Added: cs_obsolete token
Publish PHP Package / docker (push) Successful in 6s Details
2024-07-31 17:01:07 +03:00
Alexey Chubar e8ce1df2ad Fixing nested diffable POD structs being incorrectly marked as clean in GetDiff
Publish PHP Package / docker (push) Successful in 7s Details
2024-06-18 17:23:26 +03:00
Alexey Chubar 5b2a889ae9 Fixing nested diffable POD structs being incorrectly marked as clean in GetDiff
Publish PHP Package / docker (push) Successful in 7s Details
2024-06-07 18:34:53 +03:00
wrenge 4e318232c3 generation fixes
Publish PHP Package / docker (push) Successful in 5s Details
2024-05-07 17:17:04 +03:00
Pavel Shevaev 7895ef0663 Experimenting with request/response struct names for non global RPCs
Publish PHP Package / docker (push) Successful in 6s Details
2024-04-07 16:49:50 +03:00
Pavel Shevaev 3a6c447a4e Обновить CHANGELOG.md 2024-03-27 13:06:32 +03:00
Pavel Shevaev baefcfe614 Typo fixed
Publish PHP Package / docker (push) Successful in 5s Details
2024-03-27 12:57:02 +03:00
Pavel Shevaev 0678b6bac2 Fixing thread safety possible issue when reading an array of enums and fixing related bug when the target enum array property wouldn't be cleared before reading data
Publish PHP Package / docker (push) Successful in 6s Details
2024-03-27 12:51:21 +03:00
Pavel Shevaev 5b4adb4a29 Обновить README.md 2024-03-27 12:20:29 +03:00
Pavel Shevaev 467c52e6e3 Обновить CHANGELOG.md 2024-03-26 11:23:32 +03:00
Pavel Shevaev fa795772bf Using C# built-in initialization routines instead of MetaIO's methods
Publish PHP Package / docker (push) Successful in 6s Details
2024-03-26 11:21:33 +03:00
Pavel Shevaev 08d4535d4f Добавить CHANGELOG.md 2024-03-13 10:23:36 +03:00
Alexey Chubar 63f2f90e79 Array comparison generated code now takes bitfields token into account for array entries
Publish PHP Package / docker (push) Successful in 9s Details
2024-03-11 20:00:41 +07:00
Pavel Shevaev 9ac3cca2e5 Добавить .gitea/workflows/build_composer.yaml
Publish PHP Package / docker (push) Successful in 4s Details
2024-02-13 15:07:21 +03:00
wrenge 648bba5560 Better twig token handling 2024-01-11 16:06:59 +03:00
Pavel Shevaev ba06386041 Adding @alias 2023-12-08 00:21:20 +03:00
Pavel Shevaev 7bc42031ae Adding convenience codegen(..) function 2023-11-14 23:16:33 +03:00
Pavel Merzlyakov 8cf73800d4 naming changes 2023-11-09 17:14:46 +03:00
Pavel Merzlyakov 9027e248b5 nested structs within table structs supported 2023-11-09 16:31:55 +03:00
Pavel Shevaev 6e6c9820d3 Removing @bhl_bind support 2023-11-09 13:51:17 +03:00
wrenge 4d2bd5f356 Fix split. Implement @cs_embed 2023-11-09 10:42:44 +03:00
Pavel Shevaev 3f22b2fc55 Adding initial support for @cs_implements:IFoo,IBar 2023-11-08 17:08:00 +03:00
Pavel Merzlyakov 9499eae42b fix GetDiff: repair marking dirty fields for removed ids 2023-11-03 15:22:02 +03:00
Alexey Chubar be6af575c3 changed collection diff logic so it skips items where only primary ids changed 2023-10-31 19:43:38 +03:00
Pavel Shevaev 077ff32925 Adding removed null check 2023-10-20 08:38:07 +03:00
Alexey Chubar 83c6ef8c13 Added null check for removed ids 2023-10-20 09:26:40 +04:00
Alexey Chubar f7d0b5f61f cs_accessor_interface (and similar tokens) now support pre-existing C# interfaces 2023-10-19 12:54:38 +04:00
Alexey Chubar 728d6b2cf9 Fixed more than one collection of the same type causing compiler errors 2023-10-13 18:19:11 +04:00
Alexey Chubar d0df923c43 Fixed more than one collection of the same type causing compiler errors 2023-10-13 18:17:35 +04:00
Pavel Merzlyakov 1bf00c816f fix DiffCollection 2023-09-11 11:14:55 +03:00
Pavel Shevaev 09f332130a Fixing Error(..) usage 2023-08-23 14:38:05 +03:00
Pavel Shevaev 6b51e778a1 Bumping deps 2023-08-16 14:36:12 +03:00
Pavel Shevaev 6bd8abc39b Adhering to new metagen interface 2023-08-16 14:19:29 +03:00
Pavel Shevaev 945594c366 Fixing generic structs support 2023-08-08 15:06:00 +03:00
Pavel Shevaev fc4a2403da Adding convenience CloneTo(..) 2023-08-01 12:40:28 +03:00
Pavel Shevaev 2dee7aa756 Fixing generation of SetPrimaryFieldsChanged 2023-08-01 12:08:19 +03:00
Pavel Shevaev 7d7afbb2da Changes due to metagen API changes 2023-07-26 18:00:40 +03:00
Pavel Shevaev 41763bc3f7 Removing @optional support 2023-07-26 12:29:33 +03:00
Pavel Shevaev ae5cdf831e Resolving merge conflict 2023-07-25 19:08:13 +03:00
Pavel Shevaev f34cf52297 Migrating to newer client library 2023-07-25 19:06:55 +03:00
7 changed files with 424 additions and 130 deletions

View File

@ -0,0 +1,30 @@
name: Publish PHP Package
on:
push:
tags:
- 'v*'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Get tag name
run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: zip and send
run: |
ls -la
apt-get update -y
apt-get install -y zip
cd ../
zip -r ${{ gitea.event.repository.name }}.zip ${{ gitea.event.repository.name }} -x '*.git*'
curl -v \
--user composer-pbl:${{ secrets.COMPOSER_PSWD }} \
--upload-file ${{ gitea.event.repository.name }}.zip \
https://git.bit5.ru/api/packages/bit/composer?version=${{ env.TAG }}

17
CHANGELOG.md Normal file
View File

@ -0,0 +1,17 @@
## v4.10.0
- Added @flt_i18n token to support translations
## v4.9.2
- Added @cs_obsolete token, that mark fields with [Obsolete]. @cs_obsolete:"Comment" will add summary with "Comment" to field
## v4.9.1
- Fixing nested diffable POD structs being incorrectly marked as clean in GetDiff
## v4.8.2
- Fixing thread safety possible issue when reading an array of enums
- Fixing bug when the target enum array property wouldn't be cleared before reading data
## v4.8.0
- Using C# built-in initialization routines instead of MetaIO's methods
## v4.7.0
- Array comparison generated code now takes bitfields token into account for array entries

View File

@ -2,12 +2,9 @@ This package is used for code generation of C# meta structs using Twig templates
Usage example:
$twig = \metagen_cs\get_twig();
file_put_contents('bundle.cs',
$twig->render("codegen_bundle.twig",
[
'namespace' => 'BitGames.Autogen',
'meta' => get_meta()
]
)
);
$code = \metagen_cs\codegen(null, get_meta(),
[
'namespace' => 'BitGames.Autogen'
]);
file_put_contents('bundle.cs', $code);

View File

@ -5,7 +5,7 @@
"require": {
"php": ">=7.4",
"twig/twig" : "v3.4.3",
"bit/metagen" : "^v2.0.2"
"bit/metagen" : "^v3.0.0"
},
"autoload": {
"files": [

View File

@ -2,7 +2,23 @@
namespace metagen_cs;
use Exception;
function get_twig(array $inc_path = [])
function codegen(?string $cache_dir, \mtgMetaInfo $meta, array $options = []) : string
{
$twig = get_twig();
if(!empty($cache_dir))
$twig->setCache($cache_dir);
$twig->addGlobal('meta', $meta);
$options['meta'] = $meta;
if(!isset($options['namespace']))
$options['namespace'] = 'BitGames.Autogen';
return $twig->render('codegen_bundle.twig', $options);
}
function get_twig(array $inc_path = []) : \Twig\Environment
{
array_unshift($inc_path, __DIR__ . "/../tpl/");
$loader = new \Twig\Loader\FilesystemLoader($inc_path);
@ -19,26 +35,27 @@ function get_twig(array $inc_path = [])
return $twig;
}
function supported_tokens()
function supported_tokens() : array
{
return [
'POD',
'default',
'optional',
'alias',
'virtual',
'bitfields',
'cloneable',
'virtual',
'id',
'owner',
'pkey',
'obscured',
'diffable',
'bhl_bind',
//TODO: this should be in a different package as a plugin
'table_pkey',
'cs_embed',
'cs_implements',
'cs_attributes',
'cs_accessor_interface',
'cs_propget_interface',
'cs_propset_interface',
'cs_propgetset_interface',
'cs_obsolete',
//TODO:
//'i18n'
];
@ -61,7 +78,7 @@ function _add_twig_support(\Twig\Environment $twig)
$twig->addFunction(new \Twig\TwigFunction('has_token',
function($o, $token)
{
return $o->hasToken($token);
return (($o instanceof \mtgMetaUnit) || ($o instanceof \mtgMetaField)) && $o->hasToken($token);
}
));
$twig->addFunction(new \Twig\TwigFunction('has_token_in_parent',
@ -85,6 +102,26 @@ function _add_twig_support(\Twig\Environment $twig)
return $v;
}
));
$twig->addFunction(new \Twig\TwigFunction('field_index',
function(\mtgMetaStruct $s, string $f): int
{
$idx = get_field_index($s, $f);
if($idx == -1)
{
throw new Exception("field `$f` not found in `{$s->getName()}`");
}
return $idx;
}
));
$twig->addFunction(new \Twig\TwigFunction('find_struct',
function(string $name) use ($twig): \mtgMetaStruct
{
$meta = $twig->getGlobals()['meta'];
$unit = $meta->findUnit($name);
return $unit->object;
}
));
$twig->addFilter(new \Twig\TwigFilter('cs_type',
function($type)
{
@ -136,13 +173,13 @@ function _add_twig_support(\Twig\Environment $twig)
$twig->addFunction(new \Twig\TwigFunction('get_diff_related_units',
function($o)
{
return get_diff_related_units($o);
return get_diff_all_related_structs($o);
}
));
$twig->addFunction(new \Twig\TwigFunction('get_all_accessor_interfaces',
$twig->addFunction(new \Twig\TwigFunction('get_all_declarable_accessor_interfaces',
function($o)
{
return get_all_accessor_interfaces($o);
return get_all_declarable_accessor_interfaces($o);
}
));
$twig->addFunction(new \Twig\TwigFunction('get_accessor_interfaces',
@ -160,7 +197,7 @@ function cs_type(\mtgType $type)
else if($type instanceof \mtgArrType)
return "List<" . cs_simple_type($type->getValue()) . ">";
else
throw new Exception("Unknown type '{$type}'");
throw new Exception("Unknown type '{$type->getName()}'");
}
function cs_simple_type(\mtgType $type)
@ -200,7 +237,7 @@ function cs_simple_type(\mtgType $type)
}
throw new Exception("Unknown type '{$type}'");
}
if($type->hasToken("bhl_native_class"))
if($type instanceof \mtgUserType && $type->hasToken("bhl_native_class"))
return $type->getToken("bhl_native_class");
return $type->getName();
}
@ -238,7 +275,7 @@ function cs_type_prefix(\mtgType $type)
case "blob":
return "Blob";
default:
throw new Exception("Unknown type '{$type}'");
throw new Exception("Unknown type '{$type->getName()}'");
}
}
@ -314,7 +351,7 @@ function var_reset($name, \mtgType $type, $default = null)
}
else if($type instanceof \mtgArrType)
{
$str = "Meta.ClearList(ref $name);";
$str = "if($name == null) $name = new(); else $name.Clear();";
if($default)
{
@ -352,9 +389,9 @@ function var_reset($name, \mtgType $type, $default = null)
{
$is_pod = $type->hasToken('POD');
if($is_pod)
$str .= "$name.reset(); ";
$str .= "$name.Reset(); ";
else
$str .= "Meta.Reset(ref $name); ";
$str .= "if($name == null) $name = new(); else $name.Reset(); ";
}
if($default)
@ -375,7 +412,7 @@ function var_reset($name, \mtgType $type, $default = null)
}
}
else
throw new Exception("Bad type '$type'");
throw new Exception("Bad type '{$type->getName()}'");
return $str;
}
@ -386,46 +423,46 @@ function is_null_str($default)
function var_sync($fname, \mtgType $type, $buf, array $tokens, $opts)
{
$optional = array_key_exists('optional', $tokens);
if($optional)
$opts .= " | MetaSyncFieldOpts.SKIP_OPTIONAL";
$key_name = array_key_exists('alias', $tokens) ? $tokens['alias'] : $fname;
$str = '';
if($type instanceof \mtgBuiltinType)
{
$str .= "Meta.Sync({$buf}, ref {$fname}, {$opts});\n";
$str .= "MetaIO.Sync({$buf}, ref {$fname}, \"{$key_name}\", {$opts});\n";
}
else if($type instanceof \mtgMetaStruct)
{
if(array_key_exists('virtual', $tokens))
$str .= "{$fname} = ({$type->getName()})Meta.SyncGeneric({$buf}, {$fname}, {$opts});\n";
$str .= "{$fname} = MetaIO.SyncGeneric({$buf}, {$fname}, \"{$key_name}\", {$opts});\n";
else
$str .= "Meta.Sync({$buf}, ref {$fname}, {$opts});\n";
$str .= "MetaIO.Sync({$buf}, ref {$fname}, \"{$key_name}\", {$opts});\n";
}
else if($type instanceof \mtgMetaEnum)
{
$str .= "int __tmp_{$fname} = (int)$fname;\n";
$str .= "Meta.Sync({$buf}, ref __tmp_{$fname}, {$opts});\n";
$str .= "if($buf.is_read) {$fname} = ({$type->getName()})__tmp_{$fname};\n";
$str .= "MetaIO.Sync({$buf}, ref __tmp_{$fname}, \"{$key_name}\", {$opts});\n";
$str .= "if($buf.is_read) {$fname} = (".cs_type($type).")__tmp_{$fname};\n";
}
else if($type instanceof \mtgArrType)
{
if(array_key_exists('virtual', $tokens))
$str .= "Meta.SyncGeneric({$buf}, {$fname}, {$opts});\n";
$str .= "MetaIO.SyncGeneric({$buf}, {$fname}, \"{$key_name}\", {$opts});\n";
else
{
if($type->getValue() instanceof \mtgMetaEnum)
{
$str .= "Meta.tmp_enums_list.Clear(); if(!{$buf}.is_read) { foreach(var _enum_tmp in {$fname}) Meta.tmp_enums_list.Add((int)_enum_tmp); }\n";
$str .= "Meta.Sync({$buf}, Meta.tmp_enums_list, {$opts});\n";
$str .= "if({$buf}.is_read) foreach(var _int_tmp in Meta.tmp_enums_list) { {$fname}.Add(({$type->getValue()->getName()}) _int_tmp); }\n";
$str .= "{\n";
$str .= "var tmp_enums_list = new List<int>(); if(!{$buf}.is_read) { foreach(var _enum_tmp in {$fname}) tmp_enums_list.Add((int)_enum_tmp); }\n";
$str .= "MetaIO.Sync({$buf}, tmp_enums_list, \"{$key_name}\", {$opts});\n";
$str .= "if({$buf}.is_read) { {$fname}.Clear(); foreach(var _int_tmp in tmp_enums_list) {$fname}.Add(({$type->getValue()->getName()}) _int_tmp); }\n";
$str .= "}\n";
}
else
$str .= "Meta.Sync({$buf}, {$fname}, {$opts});\n";
$str .= "MetaIO.Sync({$buf}, {$fname}, \"{$key_name}\", {$opts});\n";
}
}
else
throw new Exception("Unknown type '$type'");
throw new Exception("Unknown type '{$type->getName()}'");
return $str;
}
@ -475,17 +512,8 @@ function fields_count_self(\mtgMetaStruct $struct)
function is_primary_field(\mtgMetaStruct $struct, $fld)
{
$primary_fields = array();
if($struct->hasToken("id"))
$primary_fields[] = $struct->getToken("id");
if($struct->hasToken("owner"))
$primary_fields[] = $struct->getToken("owner");
if($struct->hasToken("pkey"))
{
foreach(explode(",", $struct->getToken("pkey")) as $name)
$primary_fields[] = $name;
}
if($struct->hasToken("table_pkey"))
$primary_fields = explode(",", $struct->getToken("table_pkey"));
return in_array($fld->getName(), $primary_fields);
}
@ -512,6 +540,36 @@ function get_diff_related_units(\mtgMetaStruct $struct)
return $result;
}
function get_diff_all_related_structs(\mtgMetaStruct $struct): array
{
$result = array_reduce($struct->getFields(), function(array $structs, \mtgMetaField $field) {
if($field->hasToken('nodiff'))
{
return $structs;
}
$type = $field->getType();
if($type instanceof \mtgArrType)
{
$type = $type->getValue();
}
if($type instanceof \mtgMetaStruct)
{
$structs[] = $type;
}
return $structs;
}, []);
foreach($result as $s)
{
$result = array_merge(get_diff_all_related_structs($s), $result);
}
return $result;
}
class AccessorInterfaceField
{
public $field;
@ -550,9 +608,26 @@ class AccessorInterface
}
}
//NOTE: by default, accessor interface declarations will be generated in the autogen bundle.
//If you want your metagen struct to implement some existing C# inteface
//use quotes and prefix the fully-qualified interface name with "!", e.g. like this:
//
// ticket : string @cs_accessor_interface:"!BitGames.ServerIntegration.IRpcWithAuth"
//
function get_accessor_interfaces(\mtgMetaStruct $struct)
{
return get_accessor_interfaces_ex($struct, /* declarable_only = */ false);
}
function get_accessor_interfaces_declarable(\mtgMetaStruct $struct)
{
return get_accessor_interfaces_ex($struct, /* declarable_only = */ true);
}
function get_accessor_interfaces_ex(\mtgMetaStruct $struct, $declarable_only)
{
$ifs = array();
$external_mark = '"!';
foreach($struct->getFields() as $field)
{
$is_accessor = $field->hasToken('cs_accessor_interface');
@ -572,6 +647,14 @@ function get_accessor_interfaces(\mtgMetaStruct $struct)
elseif($is_propgetset)
$ai->cs_interface_name = $field->getToken('cs_propgetset_interface');
$is_external = strpos($ai->cs_interface_name, $external_mark) === 0;
if($is_external)
{
$ai->cs_interface_name = trim($ai->cs_interface_name, $external_mark);
if($declarable_only)
continue;
}
if(array_key_exists($ai->getUID(), $ifs))
$ai = $ifs[$ai->getUID()];
@ -592,19 +675,32 @@ function get_accessor_interfaces(\mtgMetaStruct $struct)
return $ifs;
}
function get_all_accessor_interfaces(\mtgMetaInfo $info)
function get_all_declarable_accessor_interfaces(\mtgMetaInfo $info)
{
$all = array();
foreach($info->getUnits() as $unit)
{
if($unit->object instanceof \mtgMetaStruct)
$all = array_merge($all, get_accessor_interfaces($unit->object));
$all = array_merge($all, get_accessor_interfaces_declarable($unit->object));
else if($unit->object instanceof \mtgMetaRPC)
{
$all = array_merge($all, get_accessor_interfaces($unit->object->getReq()));
$all = array_merge($all, get_accessor_interfaces($unit->object->getRsp()));
$all = array_merge($all, get_accessor_interfaces_declarable($unit->object->getReq()));
$all = array_merge($all, get_accessor_interfaces_declarable($unit->object->getRsp()));
}
}
return $all;
}
function get_field_index(\mtgMetaStruct $struct, string $field_name): int
{
$idx = -1;
foreach($struct->getFields() as $field)
{
$idx++;
if($field->getName() == $field_name)
{
return $idx;
}
}
return -1;
}

View File

@ -1,6 +1,7 @@
//THIS FILE IS GENERATED AUTOMATICALLY, DO NOT TOUCH IT!
using System.Collections.Generic;
using metagen;
using System;
{%- import "macro.twig" as macro -%}

View File

@ -1,4 +1,3 @@
{% macro decl_units(meta) %}
{%- for u in meta.getunits ~%}
@ -11,7 +10,7 @@
{%- endif ~%}
{%- endfor ~%}
{%- for ai in get_all_accessor_interfaces(meta) ~%}
{%- for ai in get_all_declarable_accessor_interfaces(meta) ~%}
{{ _self.decl_accessor_interface(ai) }}
{%- endfor ~%}
@ -20,7 +19,7 @@
{% macro decl_struct(o, extra = '') %}
{% set pkey_fields = token(o, 'table_pkey')|split(',') %}
{% set pkey = o.fields|filter(f => f.name in pkey_fields ) %}
{% 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)}}
@ -32,6 +31,8 @@
{{_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}};
@ -44,58 +45,45 @@ public {{_self.struct_type(o)}} {{o.name}} {{_self.base_struct_class(o)}}
{{_self.comment_POD_begin(o)}}
public {{o.name}}()
{
reset();
Reset();
}
{{_self.comment_POD_end(o)}}
public {{_self.override(o)}} void reset()
public {{_self.override(o)}} void Reset()
{
{{_self.struct_fields_reset(o)}}
}
{{_self.comment_non_cloneable_begin(o)}}
public {{_self.virtual_clone(o)}} void copy(IMetaStruct other)
public void CloneTo(ref {{o.name}} dst)
{
copyFrom(({{o.name}})other);
MetaIO.Clone(this, ref dst, AutogenBundle.createById);
}
public void copyFrom({{o.name}} other)
public {{_self.virtual_clone(o)}} IMetaStruct Clone()
{
var ctx = Meta.PrepareForClone(ref other);
ctx.factory = AutogenBundle.createById;
ctx.reader.BeginContainer();
reset();
syncFields(ctx);
ctx.reader.EndContainer();
{%- if has_token(o, 'bitfields') ~%}
fields_mask = other.fields_mask;
{%- endif ~%}
}
public {{_self.virtual_clone(o)}} IMetaStruct clone()
{
var copy = new {{o.name}}();
copy.copy(this);
return copy;
var dst = new {{o.name}}();
CloneTo(ref dst);
return dst;
}
{{_self.comment_non_cloneable_end(o)}}
public {{_self.override(o)}} void syncFields(MetaSyncContext ctx)
public {{_self.override(o)}} void SyncFields(MetaSyncContext ctx)
{
{{_self.sync_fields(o)}}
}
public {{_self.override(o)}} int getFieldsCount()
public {{_self.override(o)}} int GetFieldsCount()
{
return {{fields_count(o)}};
}
public {{_self.override(o)}} int getWritableFieldsCount()
public {{_self.override(o)}} int GetDirtyFieldsCount()
{
{%~ if has_token(o, 'bitfields') ~%}
return fields_mask.GetDirtyFieldsCount() + Meta.MASK_HEADER_FIELDS_COUNT;
return fields_mask.GetDirtyFieldsCount();
{% else %}
return {{fields_count(o)}};
return GetFieldsCount();
{%- endif ~%}
}
@ -142,7 +130,12 @@ public class {{o.name}}Comparer : IComparer<{{o.name}}>
{
int result;
{% for f in pkey %}
{% set comparable = pkey %}
{% if comparable|length > 1 %}
{% set comparable = comparable [1:] %}
{% endif %}
{% for f in comparable %}
{% if not loop.first %}
if(result == 0)
{% endif %}
@ -167,12 +160,23 @@ public FieldsMask fields_mask;
{%- macro decl_struct_field(o, f) -%}
{{_self.attributes(f)}}
public {{f.type|cs_type|obscure_type(f)}} {{f.name}} {% if not has_token(o, 'POD') -%} {{_self.decl_init_value(f)}} {%- endif -%};
{% 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 -%} = ""{%- endif -%}
{%- 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*/
@ -184,10 +188,15 @@ public {{f.type|cs_type|obscure_type(f)}} {{f.name}} {% if not has_token(o, 'POD
{% macro struct_fields_reset(o) %}
{%- if o.parent ~%}
base.reset();
base.Reset();
{%- endif -%}
{%- for f in o.fields ~%}
{{var_reset(f.name, f.type, token_or(f, 'default', null))}}
{% 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();
@ -195,6 +204,12 @@ ResetFieldMask();
{% endmacro %}
{%- macro attributes(o) %}
{% if has_token(o, "cs_obsolete") %}
/// <summary>
/// {{token(o, "cs_obsolete")}}
/// </summary>
[Obsolete]
{% endif %}
{%- if has_token(o, 'cs_attributes') -%}
{%- for attr in token(o, 'cs_attributes')|split(',') -%}
[{{attr}}]
@ -213,6 +228,12 @@ ResetFieldMask();
: {{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 -%}
@ -267,14 +288,18 @@ override
{%- macro sync_fields(o) -%}
{%- if has_token(o, 'bitfields') ~%}
var bitctx = new Meta.BitfieldsContext(ctx, fields_mask);
var bitctx = new MetaIO.BitfieldsContext(ctx, fields_mask);
bitctx.SyncMaskHeader();
{%- endif -%}
{%- if o.parent ~%}
base.syncFields(ctx);
base.SyncFields(ctx);
{%- endif -%}
{%- for f in o.fields ~%}
{{var_sync(f.name, f.type, 'ctx', f.tokens, get_sync_opts(o, 'bitctx'))}}
{% 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 -%}
@ -286,14 +311,34 @@ base.syncFields(ctx);
}
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) ~%}
Meta.SetFieldDirty(ref fields_mask, {{loop.index0}});
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)}});
@ -330,38 +375,45 @@ public enum {{o.name}}
}
{% endmacro %}
{% macro decl_rpc(o) %}
{% 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()
public int GetCode()
{
return {{o.code}};
}
public void setError(IRpcError error)
public void SetError(IRpcError error)
{
this.error = error;
}
public IRpcError getError()
public IRpcError GetError()
{
return error;
}
public IMetaStruct getRequest()
public IMetaStruct GetRequest()
{
return req as IMetaStruct;
}
public IMetaStruct getResponse()
public IMetaStruct GetResponse()
{
return rsp as IMetaStruct;
}
@ -374,9 +426,9 @@ public class {{o.name}} : IRpc
public bool GetDiff({{o.name}} old, {{o.name}} diff = null, {{o.name}}RemovedIds removed = null)
{
if(diff != null)
diff.reset();
diff.Reset();
if(removed != null)
removed.reset();
removed.Reset();
bool no_changes = true;
{% set field_idx = -1 %}
@ -384,20 +436,25 @@ public class {{o.name}} : IRpc
{% set field_idx = field_idx + 1 %}
{%- if not has_token(f, 'nodiff') -%}
{{_self.field_diff(f, field_idx)}}
{{_self.field_diff(f, field_idx, o)}}
{%- endif -%}
{% endfor %}
return !no_changes;
}
{% set unique_diff_units = [] %}
{% for u in get_diff_related_units(o) %}
{{_self.diff_methods_for(u)}}
{% 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);
@ -421,13 +478,14 @@ public class {{o.name}} : IRpc
if(old < item)
{
removed.Add(old.MakeId());
if(removed != null)
removed.Add(old.MakeId());
old_i++;
has_diff = true;
}
else if(old > item)
{
item.SetDirtyMask();
item.SetDirtyMaskDeep();
if(changed != null)
changed.Add(item);
i++;
@ -435,7 +493,7 @@ public class {{o.name}} : IRpc
}
else
{
if(!{{_self.compare_func_name(u)}}(ref item, old))
if(!{{_self.compare_func_name(u)}}(ref item, old) && item.IsContentDirty())
{
if(changed != null)
changed.Add(item);
@ -451,7 +509,7 @@ public class {{o.name}} : IRpc
for(; i < items.Count; i++)
{
var item = items[i];
item.SetDirtyMask();
item.SetDirtyMaskDeep();
if(changed != null)
changed.Add(item);
has_diff = true;
@ -460,17 +518,19 @@ public class {{o.name}} : IRpc
//old items leftovers are considered removed
for(; old_i < old_items.Count; old_i++)
{
removed.Add(old_items[old_i].MakeId());
if(removed != null)
removed.Add(old_items[old_i].MakeId());
has_diff = true;
}
//if(changed.Count > 0)
// Meta.LogDebug("Changed in collection {{u.name}}");
// MetaIO.LogDebug("Changed in collection {{u.name}}");
//if(removed_ids.Count > 0)
// Meta.LogDebug("Removed in collection {{u.name}}");
// 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)
{
@ -498,33 +558,127 @@ public class {{o.name}} : IRpc
{{has_token(o, 'bitfields')?'CompareAndMarkFields':'IsEqual'}}
{%- endmacro %}
{%- macro field_compare(o, f, field_idx) -%}
{% 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;
{% if f.type is instanceof('\\mtgArrType') ~%}
//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<a.{{f.name}}.Count;++i)
{
{%- if f.type.value is instanceof('\\mtgMetaStruct') ~%}
var tmp_{{f.name}} = a.{{f.name}}[i];
if(!{{_self.compare_func_name(f.type.value)}}(ref tmp_{{f.name}}, b.{{f.name}}[i]))
{
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
is_equal = false;
break;
}
{%- else -%}
if(a.{{f.name}}[i] != b.{{f.name}}[i])
{
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
is_equal = false;
break;
}
{%- endif -%}
}
}
{%- endmacro %}
{% macro field_compare_array_plain(o, f, field_idx) %}
if(a.{{f.name}} == null ||
b.{{f.name}} == null ||
a.{{f.name}}.Count != b.{{f.name}}.Count)
return false;
for(int i=0;i<a.{{f.name}}.Count;++i)
{
{%- if f.type.value is instanceof('\\mtgMetaStruct') ~%}
if(!IsEqual(ref a.{{f.name}}[i], b.{{f.name}}[i]))
var tmp_{{f.name}} = a.{{f.name}}[i];
if(!{{_self.compare_func_name(f.type.value)}}(ref tmp_{{f.name}}, b.{{f.name}}[i]))
return false;
{%- else -%}
if(a.{{f.name}}[i] != b.{{f.name}}[i])
if(a.{{f.name}}[i] != b.{{f.name}}[i])
return false;
{%- endif -%}
}
{%- endmacro %}
{%- macro field_compare(o, f, field_idx) -%}
{% if f.type is instanceof('\\mtgArrType') ~%}
{% if has_token(o, 'bitfields') ~%}
{% if has_token(f.type.value, 'bitfields') ~%}
{{_self.field_compare_array_bitfields_nested_diffable(o, f, field_idx)}}
{% else ~%}
{{_self.field_compare_array_bitfields_nested_plain(o, f, field_idx)}}
{% endif ~%}
{% else ~%}
{{_self.field_compare_array_plain(o, f, field_idx)}}
{% endif ~%}
{% elseif f.type is instanceof('\\mtgMetaStruct') ~%}
if(!IsEqual(ref a.{{f.name}}, b.{{f.name}}))
var tmp_{{f.name}} = a.{{f.name}}[i];
if(!{{_self.compare_func_name(f.type.value)}}(ref tmp_{{f.name}}, b.{{f.name}}))
{% if has_token(o, 'bitfields') ~%}
{
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
is_equal = false;
}
{% else ~%}
return false;
{% endif ~%}
{% elseif f.type is instanceof('\\mtgBuiltinType') and f.type.isstring ~%}
if((a.{{f.name}} == null ? "" : a.{{f.name}})
!= (b.{{f.name}} == null ? "" : b.{{f.name}}))
{% if has_token(o, 'bitfields') ~%}
{
Meta.SetFieldDirty(ref a.fields_mask, {{field_idx}});
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
is_equal = false;
}
{% else ~%}
@ -534,7 +688,7 @@ public class {{o.name}} : IRpc
if(a.{{f.name}} != b.{{f.name}})
{% if has_token(o, 'bitfields') ~%}
{
Meta.SetFieldDirty(ref a.fields_mask, {{field_idx}});
MetaIO.SetFieldDirty(ref a.fields_mask, {{field_idx}});
is_equal = false;
}
{% else ~%}
@ -544,11 +698,11 @@ public class {{o.name}} : IRpc
{% endmacro %}
{%- macro field_diff(f, field_idx) -%}
{%- macro field_diff(f, field_idx, o) -%}
{% if f.type is instanceof('\\mtgMetaStruct') %}
{%- if not has_token(f.type, 'POD') -%}
Error("Diffable struct '" ~ f.type ~ "' must be POD");
{{Error("Diffable struct '" ~ f.type ~ "' must be POD")}}
{%- endif -%}
{
@ -559,13 +713,13 @@ public class {{o.name}} : IRpc
if(diff != null)
{
diff.{{f.name}} = __tmp;
Meta.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
MetaIO.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
}
}
}
{% elseif f.type is instanceof('\\mtgArrType') %}
{%- if not has_token(f.type.value, 'POD') -%}
Error("Diffable struct '" ~ f.type.value ~ "' must be POD");
{{Error("Diffable struct '" ~ f.type.value ~ "' must be POD")}}
{%- endif -%}
if(DiffCollection({{f.name}}, old.{{f.name}},
@ -575,9 +729,9 @@ public class {{o.name}} : IRpc
{
no_changes &= false;
if(diff != null)
Meta.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
MetaIO.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
if(removed != null && removed.{{f.name}}.Count > 0)
Meta.SetFieldDirty(ref removed.fields_mask, {{field_idx}});
MetaIO.SetFieldDirty(ref removed.fields_mask, {{ field_index(find_struct(o.name ~ 'RemovedIds'), f.name) }});
}
{% elseif f.type is instanceof('\\mtgBuiltinType') %}
if(diff != null)
@ -587,10 +741,10 @@ public class {{o.name}} : IRpc
{
no_changes &= false;
if(diff != null)
Meta.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
MetaIO.SetFieldDirty(ref diff.fields_mask, {{field_idx}});
}
{% else %}
Error("Diff for field '"~ f.name ~"' is not supported");
{{Error("Diff for field '"~ f.name ~"' is not supported") }}
{% endif %}
{% endmacro %}
@ -645,4 +799,3 @@ public interface {{ai.interfacename}}
{% endfor %}
{%- endfor ~%}
{% endmacro %}