getUnits() as $u) { $o = $u->object; if($o instanceof mtgMetaStruct) { if($o->hasToken("table") && $o->hasToken("id") && $o->hasToken("owner")) { foreach($o->getFields() as $fld) $fld->setToken("optional", true); } } } } function validate_meta(mtgMetaInfo $meta) { foreach($meta->getUnits() as $u) { $o = $u->object; if($o instanceof mtgMetaStruct) { $optional_spotted = false; foreach($o->getFields() as $idx => $fld) { if(!$optional_spotted && $fld->hasToken("optional")) $optional_spotted = true; else if($optional_spotted && !$fld->hasToken("optional")) throw new Exception("Field '{$fld->getName()}' in struct '{$o->getName()}' must be marked as @optional since there are preceding optional fields"); } } } } function _meta_filter(mtgMetaInfo $meta, $opts) { $filtered = new mtgMetaInfo(); foreach($meta->getUnits() as $u) { $o = $u->object; if(($opts & META_FLT_CLIENT) != 0 && ($o->hasToken('autogen') || strpos($u->file, 'bind.meta') === false) ) { $filtered->addUnit($u); } if(($opts & META_FLT_SERVER) != 0 && strpos($u->file, 'bind.meta') === false ) { $filtered->addUnit($u); } if(($opts & META_FLT_BINDINGS) != 0 && ( strpos($u->file, 'bind.meta') !== false || $o instanceof mtgMetaEnum || ($o instanceof mtgUserType && $o->hasToken('bhl_bind'))) ) { $filtered->addUnit($u); } } return $filtered; } /** * @deps cs_autogen * @alias dlls */ function task_client_dlls() { global $GAME_ROOT; build_mono_dll( "$GAME_ROOT/dll_src/crc/", "$GAME_ROOT/Assets/Plugins/game_crc.dll" ); build_mono_dll( "$GAME_ROOT/dll_src/msgpack/", "$GAME_ROOT/Assets/Plugins/game_msgpack.dll" ); build_mono_dll( "$GAME_ROOT/dll_src/CodeStage/AntiCheatToolkit/Scripts/", "$GAME_ROOT/Assets/Plugins/game_obscured.dll", array( "UnityEngine.dll", "$GAME_ROOT/Assets/Plugins/game_crc.dll", ) ); build_mono_dll( "$GAME_ROOT/utils/metagen/targets/cs/metagen.cs", "$GAME_ROOT/Assets/Plugins/game_metagen.dll", array( "$GAME_ROOT/Assets/Plugins/game_msgpack.dll", "$GAME_ROOT/Assets/Plugins/game_obscured.dll", ), is_dev() ? "-debug" : "" ); build_mono_dll( "$GAME_ROOT/dll_src/metagen/autogen", "$GAME_ROOT/Assets/Plugins/game_autogen.dll", array( "UnityEngine.dll", "$GAME_ROOT/Assets/Plugins/game_metagen.dll", "$GAME_ROOT/Assets/Plugins/game_obscured.dll", "$GAME_ROOT/Assets/Plugins/game_msgpack.dll", ), is_dev() ? "-debug -define:DEBUG" : "" ); //$bhl_dll = bhl_build_dll(); //build_mono_dll( // "$GAME_ROOT/dll_src/bhl/autobind.cs", // "$GAME_ROOT/Assets/Plugins/game_bhl_autobind.dll", // array( // "UnityEngine.dll", // "$GAME_ROOT/Assets/Plugins/game_metagen.dll", // "$GAME_ROOT/Assets/Plugins/game_autogen.dll", // "$GAME_ROOT/Assets/Plugins/game_obscured.dll", // $bhl_dll // ), // is_dev() ? "-debug" : "" //); } /** * @deps unity_defines,cs_autogen,php_autogen */ function task_autogen() {} /** * @deps make_client_settings */ function task_cs_autogen() { global $GAME_ROOT; include_once("$GAME_ROOT/utils/metagen/metagen.inc.php"); $meta = get_meta(META_FLT_CLIENT); mtg_run(new CustomCsGenerator(), array( "codegen" => new CustomCsCodegen(), "meta" => $meta, "out-dir" => "$GAME_ROOT/dll_src/metagen/autogen" , "bundle" => "$GAME_ROOT/dll_src/metagen/autogen/bundle.cs", )); } function task_php_autogen() { global $GAME_ROOT; include_once("$GAME_ROOT/utils/metagen/metagen.inc.php"); include_once("$GAME_ROOT/utils/metagen/targets/php/php_generator.inc.php"); $meta = get_meta(META_FLT_SERVER); mtg_run(new mtgPHPGenerator(), array( "codegen" => new CustomPHPCodegen(array( 'str2num' => 'flt_str2num', 'str2hash' => 'flt_str2hash', 'hex2num' => 'flt_hex2num', 'str2stamp' => 'flt_str2stamp', 'i18n' => 'flt_i18n', )), "meta" => $meta, "out-dir" => "$GAME_ROOT/utils/autogen/" , "inc-dir" => "GAME_ROOT.'/utils/autogen/'", "bundle" => "$GAME_ROOT/utils/autogen/bundle.inc.php" )); init_php_autogen_bundle(); } class CustomPHPCodegen extends mtgPHPCodegen { function preprocessField(mtgMetaStruct $struct, mtgMetaField $field) { if($field->hasToken("i18n")) { $new_fld = new mtgMetaField('__' . $field->getName(), new mtgTypeRef(new mtgArrType(new mtgTypeRef(new mtgBuiltinType('string'))))); $new_fld->setTokens(array('default' => '[]')); $this->addTempField($struct, $new_fld); } else parent::preprocessField($struct, $field); } } class CustomCsGenerator extends mtgCsGenerator { function genBundle($OUT, array $DEPS, mtgMetaInfo $meta, mtgCsCodegen $codegen, array $units) { $str = parent::genBundle($OUT, $DEPS, $meta, $codegen, $units); $str = "using CodeStage.AntiCheat.ObscuredTypes;\n" . $str; return $str; } } class CustomCsCodegen extends mtgCsCodegen { function fillStructRepls(mtgMetaStruct $struct, &$repl) { parent::fillStructRepls($struct, $repl); if($struct->hasToken('diffable')) { $all_diff_classes = array(); $repl['%ext_methods%'] .= $this->genStructDiff($struct, $all_diff_classes); foreach($all_diff_classes as $class) $repl['%ext_methods%'] .= $this->genDiffHelpers($class); $repl['%ext_methods%'] .= $this->genRemovedIdsHelpers(); } if($struct->hasToken("id")) { $repl['%ext_methods%'] .= "\n public ulong getPrimaryId() {". "\n " . sprintf("return (ulong)%s;", $struct->getToken("id")) . "\n }"; } } function genRemovedIdsHelpers() { $str = << GetClassRemovedIds(List removed_ids, uint class_id) { if(removed_ids == null) return null; DataClassIds res; if(!FindClassRemovedIds(removed_ids, class_id, out res)) { res = new DataClassIds(); res.class_id = class_id; res.ids = new List(); removed_ids.Add(res); } return res.ids; } static bool FindClassRemovedIds(List removed_ids, uint class_id, out DataClassIds res) { for(int i=0;ihasToken('i18n')) { $new_fld = new mtgMetaField('__' . $field->getName(), new mtgTypeRef(new mtgArrType(new mtgTypeRef(new mtgBuiltinType('string'))))); $new_fld->setTokens(array('default' => '[]')); $this->addTempField($struct, $new_fld); //NOTE: renaming field, adding a prefix, real field access will be done via property $this->tempRenameField($field, '_' . $field->getName()); } else parent::preprocessField($struct, $field); } function genI18NField($field, $prop_name) { global $pluralization_mark; $type = $this->genFieldNativeType($field); return "\n\n". " public $type $prop_name {\n". " get { return plural_$prop_name(double.NaN); }\n". " set { _{$prop_name} = value; }\n". " }\n". " public {$type} plural_${prop_name}(double force_n) { return Meta.I18NPick(_{$prop_name}, __{$prop_name}, \"{$pluralization_mark}\", force_n); }\n"; } function genFieldDecl(mtgMetaStruct $struct, mtgMetaField $field) { $str = parent::genFieldDecl($struct, $field); $type = $field->getType(); if($field->hasToken('i18n')) { $prop_name = substr($field->getName(), 1); $str .= $this->genI18NField($field, $prop_name); } else if($field->hasToken('obscured')) { if($type instanceof mtgBuiltinType && $type->isUint32()) $str = str_replace(" uint ", " ObscuredUInt ", $str); else if($type instanceof mtgBuiltinType && $type->isUint64()) $str = str_replace(" ulong ", " ObscuredULong ", $str); else if($type instanceof mtgBuiltinType && $type->isInt64()) $str = str_replace(" long ", " ObscuredLong ", $str); else if($type instanceof mtgBuiltinType && $type->isFloat()) $str = str_replace(" float ", " ObscuredFloat ", $str); else throw new Exception("Not supported obscured type: " . $type); } return $str; } function genFieldDiff(mtgMetaField $field, $field_idx, $prefix, array &$all_diff_classes) { $str = ''; $type = $field->getType(); $name = $field->getName(); if($type instanceof mtgMetaStruct) { if(!$type->hasToken('POD')) throw new Exception("Diffable struct '{$type->getName()}' must be POD"); $all_diff_classes[] = $type->getName(); $str .= "{\n"; $str .= "var __tmp = {$prefix}{$name};\n"; $str .= "if(DiffOne(ref __tmp, old.{$prefix}{$name})) { no_changes &= false; if(diff != null) { diff.{$prefix}{$name} = __tmp; Meta.SetFieldDirty(ref diff.fields_mask, {$field_idx}); } }\n"; $str .= "}\n"; } else if($type instanceof mtgArrType) { $sub_type = $type->getValue(); if(!$sub_type->hasToken('POD')) throw new Exception("Diffable struct '{$sub_type->getName()}' must be POD"); $all_diff_classes[] = $sub_type->getName(); $str .= "if(DiffCollection({$prefix}{$name}, old.{$prefix}{$name}, diff == null ? null : diff.{$prefix}{$name}, removed)) { no_changes &= false; if(diff != null) { Meta.SetFieldDirty(ref diff.fields_mask, {$field_idx}); } }\n"; } else if($type instanceof mtgBuiltinType) { $str .= "if(diff != null) { diff.$name = {$prefix}{$name}; } if({$prefix}{$name} != old.{$prefix}{$name}) { no_changes &= false; if(diff != null) { Meta.SetFieldDirty(ref diff.fields_mask, {$field_idx}); } }\n"; } else throw new Exception("Diff for field '$name' is not supported"); return $str; } function genStructDiff(mtgMetaStruct $struct, array &$all_diff_classes) { $class = $struct->getName(); $str = "\n\n"; $str .= "public bool GetDiff($class old, $class diff = null, List removed = null)\n"; $str .= "{\n"; $str .= " if(diff != null) diff.reset();\n"; $str .= " bool no_changes = true;\n"; $fields = $struct->getFields(); $field_idx = -1; foreach($fields as $field) { ++$field_idx; if($field->hasToken('nodiff')) continue; $str .= $this->genFieldDiff($field, $field_idx, '', $all_diff_classes); } $str .= " return !no_changes;\n"; $str .= "}\n"; return $str; } function genDiffHelpers($class) { $struct = $this->info->findUnit($class)->object; $compare_fn_str = $this->genCompareFunc($struct); $has_bitfields_token = $struct->hasToken("bitfields"); $compare_func_name = $has_bitfields_token ? "CompareAndMarkFields" : "IsEqual"; $str = << items, List<$class> old_items, List<$class> changed, List removed) { bool has_diff = false; if(changed != null) changed.Clear(); SortByPrimaryId(ref items); SortByPrimaryId(ref old_items); 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.getPrimaryId() < item.getPrimaryId()) { var removed_ids = GetClassRemovedIds(removed, $class.STATIC_CLASS_ID); if(removed_ids != null) removed_ids.Add(old.getPrimaryId()); old_i++; has_diff = true; } else if(old.getPrimaryId() > item.getPrimaryId()) { item.SetDirtyMask(); if(changed != null) changed.Add(item); i++; has_diff = true; } else { if(!$compare_func_name(ref item, old)) { 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.SetDirtyMask(); if(changed != null) changed.Add(item); has_diff = true; } //old items leftovers are considered removed for(; old_i < old_items.Count; old_i++) { var removed_ids = GetClassRemovedIds(removed, $class.STATIC_CLASS_ID); if(removed_ids != null) removed_ids.Add(old_items[old_i].getPrimaryId()); has_diff = true; } //if(changed.Count > 0) // Meta.LogDebug("Changed in collection $class"); //if(removed_ids.Count > 0) // Meta.LogDebug("Removed in collection $class"); return has_diff; } class {$class}Compare : IComparer<$class> { public int Compare($class o1, $class o2) { var id1 = o1.getPrimaryId(); var id2 = o2.getPrimaryId(); if(id1 == id2) return 0; return id1 > id2 ? 1 : -1; } } static {$class}Compare {$class}_compare = new {$class}Compare(); static void SortByPrimaryId(ref List<$class> list) { list.Sort({$class}_compare); } public static bool $compare_func_name(ref $class a, $class b) { bool is_equal = true; $compare_fn_str return is_equal; } EOD; return $str; } function genCompareFunc(mtgMetaStruct $struct) { $has_bitfields_token = $struct->hasToken("bitfields"); $str = ''; $index = 0; if($has_bitfields_token) $str .= "a.ResetFieldMask();\n"; foreach($struct->getFields() as $field) { $str .= $this->genFieldCompare($field, $index, $has_bitfields_token); $index++; } if($has_bitfields_token) { $str .= $this->offset(3)."if(!is_equal)\n"; $str .= $this->offset(4)."a.SetPrimaryFieldsChanged();"; } return $str; } function genFieldCompare(mtgMetaField $field, $index, $has_bitfields_token) { $name = $this->genFieldName($field); $str = ''; $type = $field->getType(); if($type instanceof mtgArrType) { $str .= "if(a.{$name} == null || b.{$name} == null || a.{$name}.Count != b.{$name}.Count) return false;\n"; $str .= "for(int i=0;igetValue(); if($sub_type instanceof mtgMetaStruct) $str .= "if(!IsEqual(ref a.{$name}[i], b.{$name}[i])) return false;\n"; else $str .= "if(a.{$name}[i] != b.{$name}[i]) return false;\n"; $str .= "}\n"; } else if($type instanceof mtgMetaStruct) $str .= "if(!IsEqual(ref a.{$name}, b.{$name})) return false;\n"; else if($type instanceof mtgBuiltinType && $type->isString()) { $str .= $this->offset(3)."if((a.{$name} == null ? \"\" : a.{$name}) != (b.{$name} == null ? \"\" : b.{$name}))\n"; if($has_bitfields_token) { $str .= $this->offset(3)."{\n"; $str .= $this->offset(4)."Meta.SetFieldDirty(ref a.fields_mask, $index);\n"; $str .= $this->offset(4)."is_equal = false;\n"; $str .= $this->offset(3)."}\n"; } else $str .= "return false;\n"; } else { $str .= $this->offset(3)."if(a.{$name} != b.{$name})\n"; if($has_bitfields_token) { $str .= $this->offset(3)."{\n"; $str .= $this->offset(4)."Meta.SetFieldDirty(ref a.fields_mask, $index);\n"; $str .= $this->offset(4)."is_equal = false;\n"; $str .= $this->offset(3)."}\n"; } else $str .= "return false;\n"; } return $str; } } function init_php_autogen_bundle() { global $GAME_ROOT; include_once("$GAME_ROOT/utils/gme/utils.inc.php"); include_once("$GAME_ROOT/utils/autogen/bundle.inc.php"); } function mtg_flt_i18n($val, $name, $struct, $args) { //NOTE: if value is an array we assume it's an expression // and assign it to a special property if(is_array($val)) { $arr_name = '__' . $name; foreach($val as $v) array_push($struct->$arr_name, i18n_decode_string($v)); return ''; } else return i18n_decode_string($val); } function mtg_flt_str2num($val, $name, $struct, $args) { if(is_string($val)) { if(strlen($val) === 0) throw new Exception("Bad value, string empty, crc28 can't be generated"); //special case if(strpos($val, '@') !== 0) throw new Exception("@ expected"); $val = substr($val, 1) . '.conf.js'; $path = config_base_dir() . '/' . $val; if(!file_exists($path)) throw new Exception("No such file '$path'"); if(config_get_header($path, $proto_id, $alias)) $val = $proto_id; else $val = mtg_crc28($val); } if(!is_numeric($val)) throw new Exception("Bad value, not a number(" . serialize($val) . ")"); return 1*$val; }