From bbd08a9316f0d6996333161b0a7699787a914273 Mon Sep 17 00:00:00 2001 From: susices Date: Sat, 25 Nov 2023 10:50:25 +0800 Subject: [PATCH] =?UTF-8?q?MemoryPack=E5=BA=8F=E5=88=97=E5=8C=96=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=A0=91=E6=94=AF=E6=8C=81=20(#518)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 实体序列化支持 --- .../Analyzer/EntityHashCodeAnalyzer.cs | 82 +++++++ Share/Analyzer/Config/AnalyzeAssembly.cs | 31 ++- Share/Analyzer/Config/DiagnosticIds.cs | 4 +- Share/Analyzer/Config/DiagnosticRules.cs | 18 ++ Share/Analyzer/Share.Analyzer.csproj | 4 + .../ETEntitySerializeFormatterGenerator.cs | 208 ++++++++++++++++++ .../Share.SourceGenerator.csproj | 2 + .../Assets/Plugins/Share.SourceGenerator.dll | Bin 30208 -> 41472 bytes Unity/Assets/Scripts/Core/Entity/Entity.cs | 33 ++- .../Model/Share/EntitySerializeRegister.cs | 13 ++ .../Share/EntitySerializeRegister.cs.meta | 11 + Unity/Assets/Scripts/Model/Share/Entry.cs | 3 +- .../Scripts/Model/Share/LockStep/LSUnit.cs | 5 +- .../Scripts/Model/Share/Module/Unit/Unit.cs | 9 +- 14 files changed, 403 insertions(+), 20 deletions(-) create mode 100644 Share/Analyzer/Analyzer/EntityHashCodeAnalyzer.cs create mode 100644 Share/Share.SourceGenerator/Generator/ETEntitySerializeFormatterGenerator.cs create mode 100644 Unity/Assets/Scripts/Model/Share/EntitySerializeRegister.cs create mode 100644 Unity/Assets/Scripts/Model/Share/EntitySerializeRegister.cs.meta diff --git a/Share/Analyzer/Analyzer/EntityHashCodeAnalyzer.cs b/Share/Analyzer/Analyzer/EntityHashCodeAnalyzer.cs new file mode 100644 index 00000000000..6fb58421ce7 --- /dev/null +++ b/Share/Analyzer/Analyzer/EntityHashCodeAnalyzer.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace ET.Analyzer +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class EntityHashCodeAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(EntityHashCodeAnalyzerRule.Rule); + public override void Initialize(AnalysisContext context) + { + if (!AnalyzerGlobalSetting.EnableAnalyzer) + { + return; + } + + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction((this.CompilationStartAnalysis)); + } + + private void CompilationStartAnalysis(CompilationStartAnalysisContext context) + { + var entityHashCodeMap = new ConcurrentDictionary(); + context.RegisterSemanticModelAction((analysisContext => + { + if (AnalyzerHelper.IsSemanticModelNeedAnalyze(analysisContext.SemanticModel,UnityCodesPath.UnityModel)) + { + AnalyzeSemanticModel(analysisContext, entityHashCodeMap); + } + } )); + } + + private void AnalyzeSemanticModel(SemanticModelAnalysisContext analysisContext, ConcurrentDictionary entityHashCodeMap) + { + foreach (var classDeclarationSyntax in analysisContext.SemanticModel.SyntaxTree.GetRoot().DescendantNodes()) + { + var classTypeSymbol = analysisContext.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax); + if (classTypeSymbol!=null) + { + AnalyzeTypeSymbol(analysisContext, classTypeSymbol,entityHashCodeMap); + } + } + } + + private void AnalyzeTypeSymbol(SemanticModelAnalysisContext context, INamedTypeSymbol namedTypeSymbol,ConcurrentDictionary entityHashCodeMap) + { + var baseType = namedTypeSymbol.BaseType?.ToString(); + + // 筛选出实体类 + if (baseType!= Definition.EntityType && baseType != Definition.LSEntityType) + { + return; + } + + var entityName = namedTypeSymbol.ToString(); + var hashCode = entityName.GetLongHashCode(); + + if (entityHashCodeMap.TryGetValue(hashCode, out var existEntityName)) + { + if (existEntityName == entityName) + { + return; + } + var classDeclarationSyntax = namedTypeSymbol.DeclaringSyntaxReferences.First().GetSyntax() as ClassDeclarationSyntax; + Diagnostic diagnostic = Diagnostic.Create(EntityHashCodeAnalyzerRule.Rule, classDeclarationSyntax?.Identifier.GetLocation(), entityName,existEntityName,hashCode.ToString()); + context.ReportDiagnostic(diagnostic); + } + else + { + entityHashCodeMap[hashCode] = entityName; + } + } + } +} + diff --git a/Share/Analyzer/Config/AnalyzeAssembly.cs b/Share/Analyzer/Config/AnalyzeAssembly.cs index e32b7f0ecbd..60803dede34 100644 --- a/Share/Analyzer/Config/AnalyzeAssembly.cs +++ b/Share/Analyzer/Config/AnalyzeAssembly.cs @@ -4,15 +4,15 @@ namespace ET.Analyzer { public static class AnalyzeAssembly { - private const string DotNetCore = "Core"; - private const string DotNetModel = "Model"; - private const string DotNetHotfix = "Hotfix"; + public const string DotNetCore = "Core"; + public const string DotNetModel = "Model"; + public const string DotNetHotfix = "Hotfix"; - private const string UnityCore = "Unity.Core"; - private const string UnityModel = "Unity.Model"; - private const string UnityHotfix = "Unity.Hotfix"; - private const string UnityModelView = "Unity.ModelView"; - private const string UnityHotfixView = "Unity.HotfixView"; + public const string UnityCore = "Unity.Core"; + public const string UnityModel = "Unity.Model"; + public const string UnityHotfix = "Unity.Hotfix"; + public const string UnityModelView = "Unity.ModelView"; + public const string UnityHotfixView = "Unity.HotfixView"; public const string UnityCodes = "Unity.Codes"; public const string UnityAllModel = "Unity.AllModel"; @@ -48,14 +48,19 @@ public static class AnalyzeAssembly { DotNetModel,DotNetHotfix, }; + + public static readonly string[] AllLogicModel = + { + DotNetModel, UnityModel,UnityAllModel + }; } public static class UnityCodesPath { - private static readonly string UnityModel = @"Unity\Assets\Scripts\Model\".Replace('\\',Path.DirectorySeparatorChar); - private static readonly string UnityModelView = @"Unity\Assets\Scripts\ModelView\".Replace('\\',Path.DirectorySeparatorChar); - private static readonly string UnityHotfix = @"Unity\Assets\Scripts\Hotfix\".Replace('\\',Path.DirectorySeparatorChar); - private static readonly string UnityHotfixView = @"Unity\Assets\Scripts\HotfixView\".Replace('\\',Path.DirectorySeparatorChar); + public static readonly string UnityModel = @"Unity\Assets\Scripts\Model\".Replace('\\',Path.DirectorySeparatorChar); + public static readonly string UnityModelView = @"Unity\Assets\Scripts\ModelView\".Replace('\\',Path.DirectorySeparatorChar); + public static readonly string UnityHotfix = @"Unity\Assets\Scripts\Hotfix\".Replace('\\',Path.DirectorySeparatorChar); + public static readonly string UnityHotfixView = @"Unity\Assets\Scripts\HotfixView\".Replace('\\',Path.DirectorySeparatorChar); public static readonly string[] AllModelHotfix = { @@ -71,5 +76,7 @@ public static class UnityCodesPath { UnityModel, UnityModelView }; + + } } \ No newline at end of file diff --git a/Share/Analyzer/Config/DiagnosticIds.cs b/Share/Analyzer/Config/DiagnosticIds.cs index 583b9d34e5f..577b1570501 100644 --- a/Share/Analyzer/Config/DiagnosticIds.cs +++ b/Share/Analyzer/Config/DiagnosticIds.cs @@ -53,6 +53,8 @@ public static class DiagnosticIds public const string EntitySystemMethodNeedSystemOfAttrAnalyzerRuleId = "ET0025"; public const string FiberLogAnalyzerRuleId = "ET0026"; - + + + public const string EntityHashCodeAnalyzerRuleId = "ET0027"; } } \ No newline at end of file diff --git a/Share/Analyzer/Config/DiagnosticRules.cs b/Share/Analyzer/Config/DiagnosticRules.cs index 8ae3463d4ce..e4d78461272 100644 --- a/Share/Analyzer/Config/DiagnosticRules.cs +++ b/Share/Analyzer/Config/DiagnosticRules.cs @@ -377,4 +377,22 @@ public static class FiberLogAnalyzerRule true, Description); } + + public static class EntityHashCodeAnalyzerRule + { + private const string Title = "实体类HashCode禁止重复"; + + private const string MessageFormat = "{0} 与 {1} 类名HashCode相同:{2}, 请修改类名保证实体类HashCode唯一"; + + private const string Description = "实体类HashCode禁止重复."; + + public static readonly DiagnosticDescriptor Rule = + new DiagnosticDescriptor(DiagnosticIds.EntityHashCodeAnalyzerRuleId, + Title, + MessageFormat, + DiagnosticCategories.All, + DiagnosticSeverity.Error, + true, + Description); + } } \ No newline at end of file diff --git a/Share/Analyzer/Share.Analyzer.csproj b/Share/Analyzer/Share.Analyzer.csproj index c8848f1a6f3..b48b7ca31e0 100644 --- a/Share/Analyzer/Share.Analyzer.csproj +++ b/Share/Analyzer/Share.Analyzer.csproj @@ -21,6 +21,10 @@ true 1701;1702;RS2008 + + + + diff --git a/Share/Share.SourceGenerator/Generator/ETEntitySerializeFormatterGenerator.cs b/Share/Share.SourceGenerator/Generator/ETEntitySerializeFormatterGenerator.cs new file mode 100644 index 00000000000..08936839145 --- /dev/null +++ b/Share/Share.SourceGenerator/Generator/ETEntitySerializeFormatterGenerator.cs @@ -0,0 +1,208 @@ +using System.Collections.Generic; +using System.Text; +using ET.Analyzer; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace ET.Generator; + +[Generator(LanguageNames.CSharp)] +public class ETEntitySerializeFormatterGenerator : ISourceGenerator +{ + public void Initialize(GeneratorInitializationContext context) + { + context.RegisterForSyntaxNotifications((() => ETEntitySerializeFormatterSyntaxContextReceiver.Create())); + } + + public void Execute(GeneratorExecutionContext context) + { + + if (context.SyntaxContextReceiver is not ETEntitySerializeFormatterSyntaxContextReceiver receiver || receiver.entities.Count==0) + { + return; + } + + int count = receiver.entities.Count; + string typeHashCodeMapDeclaration = GenerateTypeHashCodeMapDeclaration(receiver); + string serializeContent = GenerateSerializeContent(receiver); + string deserializeContent = GenerateDeserializeContent(receiver); + string genericTypeParam = context.Compilation.AssemblyName == AnalyzeAssembly.DotNetModel? "" : ""; + string scopedCode = context.Compilation.AssemblyName == AnalyzeAssembly.DotNetModel? "scoped" : ""; + string code = $$""" +#nullable enable +#pragma warning disable CS0108 // hides inherited member +#pragma warning disable CS0162 // Unreachable code +#pragma warning disable CS0164 // This label has not been referenced +#pragma warning disable CS0219 // Variable assigned but never used +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. +#pragma warning disable CS8601 // Possible null reference assignment +#pragma warning disable CS8602 +#pragma warning disable CS8604 // Possible null reference argument for parameter +#pragma warning disable CS8619 +#pragma warning disable CS8620 +#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method +#pragma warning disable CS8765 // Nullability of type of parameter +#pragma warning disable CS9074 // The 'scoped' modifier of parameter doesn't match overridden or implemented member +#pragma warning disable CA1050 // Declare types in namespaces. + +using System; +using MemoryPack; + +[global::MemoryPack.Internal.Preserve] +public class ETEntitySerializeFormatter : MemoryPackFormatter +{ + static readonly System.Collections.Generic.Dictionary __typeToTag = new({{count}}) + { +{{typeHashCodeMapDeclaration}} + }; + + [global::MemoryPack.Internal.Preserve] + public override void Serialize{{genericTypeParam}}(ref MemoryPackWriter{{genericTypeParam}} writer,{{scopedCode}} ref global::{{Definition.EntityType}}? value) + { + + if (value == null) + { + writer.WriteNullUnionHeader(); + return; + } + + if (__typeToTag.TryGetValue(value.GetType(), out var tag)) + { + writer.WriteValue(global::MemoryPack.MemoryPackCode.WideTag); + writer.WriteValue(tag); + switch (tag) + { +{{serializeContent}} + default: + break; + } + } + else + { + MemoryPackSerializationException.ThrowNotFoundInUnionType(value.GetType(), typeof(global::{{Definition.EntityType}})); + } + } + + [global::MemoryPack.Internal.Preserve] + public override void Deserialize(ref MemoryPackReader reader,{{scopedCode}} ref global::{{Definition.EntityType}}? value) + { + + bool isNull = reader.ReadValue() == global::MemoryPack.MemoryPackCode.NullObject; + if (isNull) + { + value = default; + return; + } + + var tag = reader.ReadValue(); + + switch (tag) + { +{{deserializeContent}} + default: + //MemoryPackSerializationException.ThrowInvalidTag(tag, typeof(global::IForExternalUnion)); + break; + } + } +} +namespace ET +{ + public static partial class EntitySerializeRegister + { + static partial void Register() + { + if (!global::MemoryPack.MemoryPackFormatterProvider.IsRegistered()) + { + global::MemoryPack.MemoryPackFormatterProvider.Register(new ETEntitySerializeFormatter()); + } + } + } +} +"""; + context.AddSource($"ETEntitySerializeFormatterGenerator.g.cs",code); + } + + private string GenerateTypeHashCodeMapDeclaration(ETEntitySerializeFormatterSyntaxContextReceiver receiver) + { + StringBuilder sb = new StringBuilder(); + foreach (var entityName in receiver.entities) + { + sb.AppendLine($$""" { typeof(global::{{entityName}}), {{entityName.GetLongHashCode()}} },"""); + } + return sb.ToString(); + } + + private string GenerateSerializeContent(ETEntitySerializeFormatterSyntaxContextReceiver receiver) + { + StringBuilder sb = new StringBuilder(); + foreach (var entityName in receiver.entities) + { + sb.AppendLine($$""" case {{entityName.GetLongHashCode()}}: writer.WritePackable(System.Runtime.CompilerServices.Unsafe.As(ref value)); break;"""); + } + return sb.ToString(); + } + + private string GenerateDeserializeContent(ETEntitySerializeFormatterSyntaxContextReceiver receiver) + { + StringBuilder sb = new StringBuilder(); + foreach (var entityName in receiver.entities) + { + sb.AppendLine($$""" + case {{entityName.GetLongHashCode()}}: + if(value is global::{{entityName}}) + { + reader.ReadPackable(ref System.Runtime.CompilerServices.Unsafe.As(ref value)); + }else{ + value = (global::{{entityName}})reader.ReadPackable(); + } + break; +"""); + } + return sb.ToString(); + } + + class ETEntitySerializeFormatterSyntaxContextReceiver : ISyntaxContextReceiver + { + internal static ISyntaxContextReceiver Create() + { + return new ETEntitySerializeFormatterSyntaxContextReceiver(); + } + + public HashSet entities = new HashSet(); + + public void OnVisitSyntaxNode(GeneratorSyntaxContext context) + { + if (!AnalyzerHelper.IsSemanticModelNeedAnalyze(context.SemanticModel, UnityCodesPath.UnityModel)) + { + return; + } + + if (context.Node is not ClassDeclarationSyntax classDeclarationSyntax) + { + return; + } + + var classTypeSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax); + if (classTypeSymbol==null) + { + return; + } + + var baseType = classTypeSymbol.BaseType?.ToString(); + + // 筛选出实体类 + if (baseType!= Definition.EntityType && baseType != Definition.LSEntityType) + { + return; + } + + if (!classTypeSymbol.HasAttribute("MemoryPack.MemoryPackableAttribute")) + { + return; + } + + entities.Add(classTypeSymbol.ToString()); + } + } +} \ No newline at end of file diff --git a/Share/Share.SourceGenerator/Share.SourceGenerator.csproj b/Share/Share.SourceGenerator/Share.SourceGenerator.csproj index c76281a5ad1..5d9e00a4f53 100644 --- a/Share/Share.SourceGenerator/Share.SourceGenerator.csproj +++ b/Share/Share.SourceGenerator/Share.SourceGenerator.csproj @@ -18,6 +18,8 @@ Extension\%(RecursiveDir)%(FileName)%(Extension) + + diff --git a/Unity/Assets/Plugins/Share.SourceGenerator.dll b/Unity/Assets/Plugins/Share.SourceGenerator.dll index 1b7049c4e695a9a6571bb349aafac3445a8affb6..996a2e59a8c4e71c74c9a1c904877e1b44684c88 100644 GIT binary patch literal 41472 zcmeIbdwd+ll`np}XQt;Rjnp%G*_P}PevK_l_=T~JaV*<17Wge;Y_L}zOJi9eY2+CR z5VDBOD}g{30-FF~6ELtFXGxZ9AR8cAV1W%Ra1*j28}@>)xdA>&Zn7cSJT|+*{J!T@ z_jJ!_BpkE%m;1+#J=JwiojP^u)TvXas(V_SFTRyLMC8Ty{r8E!jw^rGNIH2khT_PK z$0PJe@a6iiYnxuKZ$H?RjSpnf-I-)xyffL~pB{|wPsKAs{qdgu`1-AF@xF9ds;RUz zG|OVWt%Ycl=ApJbD!y;Gc7o1`hqZ-7cLQT6?D5BNjpO?izC`7c*OlCiVENTYBLbd3 z9@=+3lk$J(YJrTx=M&(&l`~Hf-NA||{G1>v0qzS>OnYJ3WN@4);3^LQ-spmx22)oK z0)Fc*Bq)=99Qzo0~1SGOOg$#G}9(-M&H5jg@OsY2xLY9?I;LE)3#n<&& zL$uZfO7=&>?+SaqhRWtzuGSF zjDsO_UOC3YJD(`FP^Swa4XT?|b!n(7+^~#ve|j1kDhW*Q{BCI=4%4NM@5a1FyxvE2 z>BYhRT_HPfdI6BXdRN%5r)yE^q&Ma>eT`2TF{6P@15Ka}4=uqJBADcL*?vrBvw5(gq5$%o+-GiqN?iP#B=-X*RAf5z`VOWF#Pe1Nsy8F9?y5K>r#_8oU)H4HRG= zEcd0jy5&v)X!yS@yplae=WcUkKO1tOhb(&nT@E$Wkug+-eun)OCp}P7CEK`@!2^l=t})@LtGV8|o?OYK29Xc6<%>!L(3N zkh@}-M&qlwmcLXL^Py!Qgtz+ze{%Z$3gq{z!qu;jpkG0Y_aaElI$Mlus9_rVg;~Km z^CVzxM!t%cqNwq8TSYR5VBs)U9z*s6Mh3M9^z>OkCYB*liVigpPde@ww+-V3eT7Uo zA7OT%NuH9b(uV1Q;N3AV>84kPhvzIzpgN5430Cha4F*2hv-ow8^Zeua8-}UF|8d*s zjuC=^Xt979SP-O&SaA8z(3HoBhE7(_flZmA1neyo3rE9=<;bv&X|WQs#G-x@)c&D3 zC`$r7I;Cdmf(T4!1y>3#zgLPeueDMcyoeb=aWK776(5meC`04n=K5u3*@6;SQu-XB zXxQ%`f)CKRQ(RnbmUFQ$y-F3Y^AEv0XlxW*Y?>w)8|iaZ@htxkX1W&hn>;mQQ8UWL z{`6{9>_dNHJn#}czdImIsT$wvG!$+-rbNy$Iqs(NCfNiZsj^`t5%{4{PbdbD(?K~HwJR{bj zyz$%KitV+_$cssDW-&i7oAgnvfnG93_!3XS zxD)3=_m)4afV&z}WeHeNgPKpQD+9C7*Rb2~jry&r=T9Kqz{+ROPepOIsy(y^hRsGD zG}#|>-MT=!6%~w=246N)Gz}!v;{fXTKxNQ z2oAF>aQEAQY5ak0U*b;=fnQYF({^TKGM2aD~*^c6i6c~a=KBj zLL38L@zuP3{1$vFEWKdtL@yWN8e0Cc^f0jt08}FmN!%HmInM@zmVXf>LFu6D2s_;szhm7}=45@Lfj)fXBn9xzj z;lw52;P?X79D8JgxIYzShO2kQ!e$ulB7}oSZv2|*jj`+HbvvYd1wKCPJI?ZaWRnbw~OpJbad<-lO^ml1<$H0$2N?TD0#pFC7acuyqR+2=U^iLfA5_*lf#S z&^43EaGpzsX>~MEYmJjj7EBWiQjMkhDacR7cUs`6=)FL^hbI-!inS}K{h*Q$=PqgV zk>*?Dm4&R;Zkq;R*)&GQn;rnMvT4M~SgTuM&r)y*1tksfsGi6mS4z4k>PdiG2$w-z zc&$L}h4LXljsFZW^^;XGXDH`evMr;=PFr_gx*77+xa`N5zMR=NRDtak$nieUV|oo< ze3-o_aV4{V6YM=Ft6+JyrMTyc+QOGfpp`izj@dPgO5w~LWW9bvM!f{q{A-3YbL&B{ z3IE&Cm1hqmr)!etPB?oKIYZcGU>a&kYsFC3m7d{^;2eP>i;tF>54AA7Lm{c?R6w znMl%yL1AOBoK+0_X~sT+B#-u6bAbIhl05A16TmK=SAgB#0PJ2QiWcV{9yuGF$~sXJ zd7_jag(xr|*iMu(2O4R>leiivbvDNPZbZVxN@RxfzCVgShg>|<9|bJu0yjVxO)!E{ zL=^C!`bh(0rhK>Mwb>uBeNW^1w$FxfDf><%wl~saNnHgNaWfOUsc`K=v>J*}0Sk1yIpF0_;Ty^m%_$sxWGM5;8;V zALc8ZOf{Ux@HE1dwHa_N-m)P!Z+x83JJd-^6zjpKU>q*nz+sNz@%sFUrL1P&w|nWk zUZOf>3q0nijm5WwGG9kkWl&V(Y>k&9++ z43_Ukl|cG7)JS8~5{i{Y%M!P9Mvh)IIeLM{7c*y1AbkfHnNd@QZxkz_7pF3R2BiDI zFlJ;w&2)Zs38+g@T|(*-R+o~hSadm`Ch_6V8m?3otumtpo1Iw9jHN%rrN~Hhpw7Ii z&1zQv;Uaa<5}AxSLLI9x{onw162iHUSz+a3nSX^6%?dSOm1e-Kv`V5?7_S<$W3cXgFFG&r=bV{tPG>WjZ_ejk<`EY$#^k2ot5xgek@1b+&{4575Cud)PsjH*tOnVw|a&J(8bh#@R!UF(6(!R-gGa z0L+rlvs`CH>lEx4coY$iS0Mt5V5Y1z2#%OsN(7M}W)RL}zy;m|#xC&QJTNv(@eL`y zvlUdLpg9T(D`;-i6RqR(N`uV$%mXa$3~4Y{5BM7lTe@H=1o=9U!4WoJhwwMRxxmPo zsAo9EIPzvb++zp1hLN)z_)>;vI`B;lpW(pwGCbRXpJ#ZE1OF?-b1k?k*3htpyEJ_v zunp=a%f09VjJY9wA2LqGRfQzMd=fGnV5SXb1CBwDq9#rV7MKef@3Z%Ru!m|HAJ_%& zP^|n^r6jD}7NXXibrickjutEfNtF4_gjuhm1~KaU!GGn9_h9$%MGr7=@IT*wUqx(K z1)QaVngPPL34Vz_4-fPA_?98hpIjiYSlM%8GX{7*?@0{8eod{99&3L;o@rHR~;L}aQ89S`VwPM|{IKV2VTDdtbjehrtaU{(x@w5&^xyWm4u|Ca7&oLo|)kMP*YC@l%=Rcg>e z4gRX^-vWBgQU>y>?0q>*#fi__85}Q2^>T1g~ ztFwXx!Zr?OBr_3=#`+C8=2k|RD4Aq`4{n%I`=EEfEIjFN zqRyQ8TlDPTAv4Ma_GYUoyAc(~l0Z3=X=-9UkWhHOjy3^l>&QcAX+$sx(Ghn(4O+?x7DkjD!GZW6KpeP1zgk&$Z7LvCzKjB5p!^yGuj2nG}GVW ziaZq0Gcx5G3iN>Wje#VwNjV(s8aIQ5 z8FWIS7~BpEYlhgy4Y?KRrD}8)Ebm9p8XpL-e0&g&A=u_mgmVLI4a+K>Z+RHO^gn`} zX?avCaHCYP*%yILzk~z>U=h9y$e+ekhUr6|wQc9G)vTvNSjU$yZCcp0WZ{xUD*)iG zeJ|4WQ!wwZCVCjxUtvEsvu!Zb)8CzCiU$_M`W69qVH-V!p@qYunde>Dim5{10Q_te z@Y>$=ekR68hvF}qaWovnf<*tJE#bo-3=d`Sor>?J_-@1Z5`58H@i}X7y%k@Kp{%(c zT=~lj2jjTD7vCVhK76r)$raL;a2XUt!O)YYL0^;fyfEiq2^=IiXKE9nYJzb|Pt$oRGZ*S#cMhJ2`k)5ijw9zZ@sSNR@~8njTW*A03n!uT_g zkI)0vcjHdrm8FkIqx2`*)4oo+9f}IkI~vnZ1-B6OqBKJ9Rx#yYmpmQ~Q3RL}JuKVoVMCt)zW2M!%7C6g@KNxb)?VzC>Aw=Ea!L3Q6DB zLuCt;|aV3eADXqbp$L zKmvLj7RV6Kq;hyMhIF=nK%awsufW{!(Gfrg%YLY}@SRXN9)N`Uru%CbPFXRTNw3z z1&z7M=^y!ffpzZ5Ig3J)j;ybLePvU)dabHr$1DP2_Q7=h!v= z?y{X@W0D>$;ryQ(-DS(@&&^Ab-sHIynCVh_evnI_k@Wrm=X)f7rsQ`?{?8=6RxtgN zPKz*QS~x%E<@_(pIQ^eOxe9IV92=GV&!qGj4`V7N{U=G6O8O}&y<75Kk{^_OT=Lr` zKO*^$N&XQ@Lz4bh(uIyp^^(39W~puwJ-jA;$<#3B zPQkn_n2!o(q>Ax3N&3G`&c7(7ohV&K2WwcX-znjAt>oX8{C3ITEP3X(jF`_dVxAZU z$t!w?!_KiP;eV6fUABEpM=!RI?W*APUFdunEin4aG)hLZk{&KI$fy}Z`cHH{FtY-W z8yekI`6Hx5y5xOp;m5B>irJ>U0sOTZ=bzAiWWd8lZw1env^&eHVAJ=2&lk|5LEn?~ zV)H)mJnlbSR!@)V4+DR(;0@>p{w~^Gwk-}bq#J7D2v0QHorAuGdYK&b5}<2x5SM)_ z2l-I%0f9bEmA>zyEKpDQe41AK{<%!2jTZD1{f#m&y_AFAh6Ef;G36U53(&_c=wp$e z!h^kJLAQnet*nIjkw&IGFZgfpj1O4Q&qM!F2G>1Hm0jfhx3U;*vY-n9RnlP#`f^w= zucqfMXrDh=K7~&3vJIYV%{R(wY0n%5t?)<7r_bX$@22~R5MSNJ>*?o-avO+(Cy*%<&AX0f=XhW%Fm>?I1GX; zC9$35i|MTf1>F|vC_jrfEMVxinmJ~7`Pnp<2YnAQq*I0yAs6&Lf!@(R9=;6q=u9qq zM@xk><;!UnJ0m>P)D;;nUqKI8&}S-#%U4nYkrbCb4CowsULa*5tLPPh6wg)kJqu!< z=TaAa`qA>$RI)@tvwfc|Z>Fq3ipzRBoCn31GUW}_S8;dwdK$E#cx;-dg+3*a zYHtHQVnN*Ad9)s}yK3+A1x>eRuxfsw-t+Ex+=7n_TYUc8$cBp&g_ICEs(O8U37k4S(`xDBI>)_vy1i!q}QTcoY?{W{k3 zZz6wcavIo?=(GkrQ42gZx-a5zW(hUH>+~>Wu;siMQhBhoERIodIjkwx=W9nZFETZ9MhkO zbVM!0tj*1N32Yfl*M!z#}#N9jQFiSY!NW1*^QnU81?-=W45@?kn`$*xA~R^sDeH?U?>p%^KiY<~Qkg#@X7N z^d<9JZN1QM68cS2`a4==F#Yz5&uK5{zmNPtTcK5zzO8B6yT(tnprodxQ?&X1pJ?@x zpC{>U^s|_zPtlIY>h*Tvyhk|i5zbxcqmQ~IK6zR{ui_&875(4!!}=lZIsYe+{yKW6 z-V2(C^dIZ@RQ!$p3;jE>f6<4b$rJh!==L3<|0(j^i{H_!Ud?k%f2YRp`32qWtMvR% zuP9;3c{R{H&&66igCfIWk>QyB95m2Qtn+qyuXK;+ut*j3EG^p)J`Z?P9@F!$HJ5o_ zz<6EmnS$|pNPko7jNRb*vG)10yFJ&@i%HF)KHs26_1lp@96l=eze4`5ny+}D75>i(|CfaS_k{oVg#Syz zZINep**p4;TC{4FcAIFg-t$n&Y~KrdeQA^LceF}BgI0JpMppWI^ymC*d_UG&^)0^F zrFURZgAi+`jzy>DdTI`r-< z-s_~lzZYp=!I6Bce}TsGC-qEtG2J2K@{l$9JZg8KcbE7d63yHp_-Cz@NAPtkZ$xg# zXfoyN(nyX||42d zApHWhBYlVtA$^!`K>7&XhxF_82-0uRD@dQhS#gA(rH>$ep7tPpi4GzC4&8+Gd-PeP zKcFuleVx97^aOna>09&y(jU=rq(7x!BYl@5IJx*0H6s0Y+Jf}=ltN0{HAt6h_aR-S z{Ug#f+CL**uj$xFY}C#!n~OU^Pehw=&Ntg^!iqB==`>n`bSCXU+CY~eT|)OGJ%|1d z={kA==>_zCq-}z~1nCkyb@FuCI_d&w}ZOP-{2CaKE=HSs-S})Rg)F=74l+KjWnSxm(n010#Cz$m@zfbTf!Hh}% zUPJ$TXx{qQzslJr@@oRIuJALF}xOg|?1XMNo6EQ8bQBz?*_ z1Eo(HEW-&&3zoMxPEwk)8-L z=QkyPU5NXBAJTaA0m(lt>9dl)DQPmyG)E-8FFcjzM4y)Yo04iJ%=11;-z*W%B`?yV zs8-6g>LqQK^kV9xVa)blkyqQG?a>CbyR`?kzt(=Ph4l@3m;RFe8@XBZBSsbIQaY7%1FPn?>8k_~j;L#e8-aw5weOpGKqPyrXD6TEk zZq{zs8uTUlIr>q3i|0bmy`JxS0^T#ctGxTX8Sf{&|I_<_y#MKa!}pGlj51@cvC`OL z++=*!c-v_6d&f8qc7Of~hZ6|6bD!J&UtsYeYcPkq$N#wOO7d~)1U9!p0`-Ibrv;2k z@BCOVB6zMC=hBFttMRQtTs#Hesrd58q_;$vO6xkuy3Vz(4Y*!_Q>V@NZpHT&eCN>h z`e*1L^d~9f*{=O1&YD^we=Fo|MdaTK?Y0VUTD5vbN5{gBg|w=*zh|%~+1qnfYW4o2 zs6|Y*y0fEWeNT3vH#xknH<`^YW#oCO{!}J8m}(o&4yO9n;kVmshuc$qAR0`q?(XPl z8%XtZCVRJK(w(Vnc0)SToaxRUma2vP7A;c@2b-@*_6)WSfHcJ9 z7{IE2$jQ`8n9;JO+;Lj9Io&nXn_5k3D4JTgQcF8E58@8n{-MFt`s84e)}{Lf(*02C z)&l}}j)VILQ~hoG!LZZjvo77+o07(}O)}a&oz$Hg>}bvM5CPbc8c1gb*Y_m5`_oxQ zaOt|COa|?2PWC6eQ(f%`GpS@(YZv(Tc69V8bIT!H+Lg$gQ-cT7U9H*He#LoPGQ(ym z#z}g_Pd5E@DMok)v0A^Hx+0?cJ%G(dY zZ1!DUxgoGnkhBm5MzHu{i=aO>m>o>QJTqO?%A#V(Q<($F&J?wZxGq@9=R(+haHDel za3OPjap7G3abcZ4xsVt#TP92l2WZ83<$C8ryF2YdTOD^WFs)whDCoB&`%-NK479Yb z>+OMUIhg)jSvQ*=97Ym0*5Az(SyiWhuxD^snOTl~Tk6nIsvjI7Wm|eEqZ-Sb$L&EC@a7gythFz>)H+`GXu)d1hKV$XHOQcO-xTP zt5z@R=*T)Szx+bjDw_683Zv|}A!=m@oX)aiDKvs(FKM! zWYT>WroCqn_*0#Is<+T6ZE96s?0_9fSerdSNZSu724V+msd8 z{FR#4r}htZcc(IIGwCbDx@7rgi2o?512r z%A<>Oc5&#J6;kxWgElPwy09E}g=N9P=5iNilijHtWc5Kf+UlS!u%~>?xSl!;Y`cqsG6NXl_u(kg{ zI@2c$aI$woDtO}qJ>5fUp>|rZOa$>@Zmgh52iTE1(3U)q8XV?@--TBuCXMb$^(L=W z>%UXamK*_R*HGu6Q-J9xL4bp?ZG+bfxNN6DX1By`4$kJw?I-((odV0z3VyI>e@`!F zuT$D`WvUaw7p)yefNE;0jKEQ@R`BU@o*iFOwWNC=&9RZ%Ymyv>j1b z3hoXdOTQ`A-;FE`zNb@yarSbPhwEsH9lLd5HTA-A3aXX;C`WkI+}jIztZqw7d5Yjx z@)7X)j-HgH;2~NglWauG4fKFXrMl2Kj+mf6#Xv09g8{@BD}=1VJ9 zysYx)r+fOzGAkY%mW(Myd#)LZG8H~?Z|#_Bb8^5Hx^s24C`y>`E|CzxjcDD9E~ti- zv`z35*4_l(|A6c(AjOvSVB63DZ%R^KEmwA?21GUlmz|j&l}DpW5$wZgb5Ca`olPGY zY~oc>W?Z%>+oZxbdp044ID}nT1`ao!8MZck>C8G<6sbwIb_kn|R7RB03J*KjbD#&H zD&*d8O!cAzWKHRe1;X-pCt`V_9jVS#&*c`E;w3HZD!|Fb?ATSq4a>+E;{=fvOBFVw z57=O(GTcZ@dy_4VHNXX8NPLmbos&dmnOg38C7(4hT9%yRnl#8>wwao}+sJA5rhk}h}Gp0+H z>9KPw++}B})8?+W-C0|O4%aM9%ice?c4c`-B5{QBFET z>)Jg?ps8rkeb_QWC^@pjB<>$9sLUr_S!(H(Q@Z9sRv3Clo3YtRcBhn6A{CPXICpb_|TtVPr|fB=dlQrnt=Pfrf@4ieiF&JiFG7JX{zzq}`t?q`Wa6KeS=#$F7z zr_N2 z$4qvSFiT^{k1p{FAgeSFkF6ifOqiiyM76N#%Ts(#Ve6os^UOvv7(i z`bx8)&lRgE7XiSylDriReo!K9<(p($?d(Zip}>vl!2>;43Own0+@x5FuvV7iB`eF8 zVr6qiZo%%&c*~Uo8JH;~whH@PJN0pe65x)*Ci#?l9G3Tj75rWaQ$XoAVY_g8CDA;(Ko0p zPzC1hGAl2BQ$`f0-Z{t1c`G`xml(jqlpg44IW&aZ6@!v*?Z+{mf}Az9jn7Azpf$^@ z$kt3t-vA(51iz18i1gr1jolQ-U9B|kqoa>%!;paPYY{>fH5I8x9o( zxqlh7K$UK~95%02#-C1Ut;d^5-of#$==u^c!4dFt45urukmxDU#RvzxbVjWwmlp6!aY#cm3({=rpA zYulnz<8q-Kf|PNYDoV5cc<8{Uar(^enoYdvwDqd`vj}Y|``tE)WNc&k;~9snj@H?> zdC@stdsDJ3{*d(@d;P=vg0mD@h~1AkdTWmtTU$~}l#BT=ePlcSF!dM1{yt3oC74O} zc=McAmZmHzj(M8ID7t5}Gi%g%vt8QM1E9{=jPiR-qd0eqRq*WV0dIF*rCZiE)3UER z{a&4od3jc@S6f`Zo&zWq*zoK-P#^)rhHnF~#EU0y`l*K;l9*;uei$_kJX=Pm)L}Xv4JS-vK23N;n6J#0DR^v$ zv2kg)X8!{4WnJ^uW;OC1_-A{DcHrAlgIB|I@#Qu63Ot-V0?Qf2!*p)h?Y@j4rWwsE z83nAy*6ydf-hcI2+im2JdHxJz+rAuq)2$sfFK{Yo**#0lwx5Pf?2lQG?7?{kcBAaSR(p<2tKf--(Mk%l zEiukJ6!R)gMeUDgKd%%#MryaBX6grx!$jljmeBDBiJU%)kC{DoS=c^XzFNjq3x&Q)trk2RTW|ox>oFg7C{NF`;vo8!#@K8@iUV@7_I}t6ubj?IcIE`n z0yTFgnlJX+z>&^)`yIh9npcL2tgujjg|J&sfBcn!;~^eBwMVrhg2~r~9iX?pqS`&% z5U&U;#L_2T5%?V9^w)&_7+;PFI9kJvFqsJq@v5Ngo975_Pm(RAd)B-C(mbif^$J(5 zVpjP<9?F(vD}3Ljkbt9+;{L>5)!g!*o+oxJS@l2PI}~r-o_VaRQ(LR-8ury~zc!8~ zaaNX79Sye+wr5i@?th5qwLK2UZydoD<7Z2qudh?{rhNKP%sx)nYq6J7?-CrpH?g0xwP0UAXniR3V>WDG$M2ie-h)TL9u>Q-{Kz$bPoQ>| z?BUr@^FE(KL0O8gS?W^i`VFWdP5N*}v zpVs`}8caTInP@Qi6l1c%{6j{BXMnw%Ib97p#-yU4V*3X^NttJv*azvlm~r!&i}Kc! zdo6oZ?W^sbZ1{t&dDFntK{WPqu|-}v6IOJg(k5Hx-OIXiLH7OEVow3s0S?c7we@ok zY+<{0$BG}+aZOeQEt;&wVosOBF2}pbErH$QWId)}+&aD?_*f3|_$DkL(bA!~c+_6F zgo(n6Vy)S&@^rL=d54p|z2#$+{AVgw;f0}-8ITH_X-fXFv2*X8t~qZO+~Xkr@xD>r zzf9o;FvWz-SBZPrnFrs=%H~e2@N_2JyP6Z5z?s+eSvakoC3gnwyDwW%kC%&Cd5Z}z zL#INw3RL6UorM`yOnP2R`CqXzTm5`Dp3ffCu20>&nCvumA}JF^CEF>|W>Hq$G;AqG zCY8~qa{6xbT?o!i__9XFYuGK5UBiB)N1ZEjeS7z2w^Dd5z7N9`tlNY!oY2hbc&EjY zKc82oa2m^Z{JN~rfUYqoHo~AaY<$yfq8lT#Tw4prq$W1DjsgML;3gBWxS@?#o-#Z~ zVl|Ks97G>Z#lSc!8-{{9As2f#-b`k3+!x12fTJw;P};{~S-g9VCxc^N?siYjq>_mK zl9;S~#9Umbf0%iF2y>d-jNYr~b|xDcx+eU`H=R=xwYU-4-pU!Q|2CS__CaS)G!mQO z)p@gHpEeizpK1T^8qwsU|F=<{+CujsJW;p>`%3jN16|{v*iZ^r%E^|i@lnfSINXUB zEMB0Ru@u;XOg`*|l1%RW|NmT)3MI4Q$tAR@{vTSN8tkW)_!*mi1pE9HAfMInlOOy9 z4EvgtJbOX2i#>^ApAqz1_dV#W@lp7Ae&cF+u;uXJ^`)M@co4bRmX0pqcsF0a`Ib5P zxv4wKrWTFHs-Mmh4^B786|BYr4xh^{??7n$%UQkU~Or#a#vhAGQPjh zaX8I(=nuQLSI*mUTyo)rYwW1M>daK*$nl(!uTzyIys_h4pf`)TEF~V z0JU)VR`~Q(@q2Lg(PA@nkiQ?LGZpvot#8HLD?(3n^x_j+uIl{iy0NeSeEr|Q<{nH;%V%0j>pnA>crqRDmx}(XrQzxUNO& zB3#cr(i-&p!vT-ar-#7P6F{|q*RSgVgFKN)ZEcy~$91Ev(N=vr{clbr_zw$hh}%{ z_>UeT3sFqG{;sFeOd^f;KuYvX07G2_PNBNRa<;P6MH>=4R4Nzt|F13-_Z5CNFbso{4vNo8XY?l9lJg{c4Ktx zmgv}R(Xl_%qle6~yUejaN4xwfArFA)aI_U@mUfhrL#AJblC?gaMDM}4=mtuxv_u=b zUSwnb*XugYbSw~EgnGuVS6v;u9-KBoyB;IjTJHA;FbKH;)l14Xa98RMRQp5GjnT13 zqhpUn$Btp}j_D8z9p{m3HIMZ9Fk)kmLS&|BjSizU*X*%iWpwOispNxw)z-umYskV4 zpa>li2FFVMK6C78bL^Yu*mJrL%RyGsrzL%kEeAztFyuF)V=qLYC)hen_XRI$dL-cY ziITZ$^avCN+$?{Y49BQ+ew0;z3+va?1v&x19>GyHf_%XUj$#^Nj_8I5f=4_aqg;?ndp(y5`QqdEx-klh!VmFC6B$wGZGTrW)M@?vK}IlQolDk z_Pa{+2qr8{gog=Pu~0@~7NR)fCoj*o+8LnU1F4Tx>XFLmktv$F3qV~CC@rj=uAq16cL)T0S=m)~kk$yX2gU_MgDN{K|^5+ayH>^GPc zB1HV?9ye_Gy%?cLAdZgB=HBPSbuM^{ zzchjgvzIM{&4f)fdgLq_UmXQUM)Ceh4=g}UDjAadN`X0YR&?y`=#l&CYQ<$9Icw|& zeqSE@?(dBd+uKivQ-p=#dr>Ss$xz z3oH{35yk@n0zir|BJ{D446n4nC6bYnM`3QT4Osy2Hg6%5W|&MeY8;!{uz?a$9BwT( zw-({{7$zKKhYw~q2{VLx>LPWXupcf*=JYF$dwIp4=db8d%y4vc+ysAx$6i*3O=w>? z0(cu^0B$ni^Lx-+Gp=)6?1q?mtFGJ54hG{fAThVgRegS_&R2%nwmZ5PHYP(YYr!x( zp}joV@TGS(_@i(~pn>0JI$H^fo+UJoEW&?q*kw5^K;kEHMQ>whY@y!-ld(s6xyf;` zbtfa(!qbHZp@lt*9(ZXa_WsL_Z)^`NdUE;BFD=`0R^Z`}bbQo&HMBCS)_eDPVpZ_~ zqlD4+0a>n^gQ`{4TFHHf%Z`=es&Fj=HNFozK9HED2;6nY6D?FkEUX>EaL{A#OvhJ9 zs0tepXY#Z7d=gqbj1McxYHW=#Q@72yEQA$SSUg?}OKmNfHgT=@%%okiKF*4bP&l0t?9{_nqkdE z1=DQ_6vM)&9IJ|>ty$4(-K-nGdr)IEee$i1{FyF(4u_B8`1#0yoyqx@O_omFDyb-j zoND(&Jr3di?M8Gws`Y{LpgRYK8 zVlwcCkk!_fXE^1B2yx-Obxh+BCBUP^w?RYB@1INDldi&Kg3%f{cU9{;OlQJ4L5>iMi?w^>a5 zYico=Lee!K@MCI`s<2Z2ueH@Lq&-HRPdD&Cf?ULlZO7XI4zf7V#m;FBW4;vPD4+K@ zuSm$k0YA#xQFf#>Vo^(AX6`CipcBRVG=8TuZ`*wckF+|Um$Xm>@m2u)8QBCQV#3}T zJ3hYb<_qH5z1SJ(64o$?=o_1{=%MIPbQm!%B)KJ0j;$W=qSYoY+RBmc*fB^O9s5dj z7$G|X8V=l}d%+3uaa|;Id2u!Us>{&)OyGidVho zeRj01Z+qg*Pkb%((qo%$n*NEe9eAb@<4$zm$~}0Y9>08ZPuuw|?R#WkR9g6U!9D5y zm+q0#DPG<*(6yi1Ha0IlYZ+Nh-E23--Pv&0C%;vH!<|=u=Cfa~`zl1XKQD7WZbSLk zCtY+zcp-9TJzkE8cOp|?wiEA|P01?{rOx}a!KAR;`P}j#{(GE+_teu^9vVi%dV$f6 z!!zARe0MzsGn6;yGluj0hv0U?KX)K)!MfRo^>8bG?9hSh7R13@h3=c)pPy9k|b>$4tL{LM0~z!{0BIp*a!?*Nuh{>+kE{8KXyqO*Acv=Gq4-aElm zgG}nMm48&pk$FC+L80Ln5U77k@sJR7fYqA+lA|3`aT~+vk$Rkm`M1G3SvG#Qvkfxw zkLft_9KXU5Rxsvex2I08IM5m4nYMj(#( e;EC(-nEVkw4`W$DvnN*k5!(LmCjaOf`2PU>l-wTx delta 12710 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 this.InstanceId == 0; @@ -162,6 +168,7 @@ protected bool IsNew private Entity parent; // 可以改变parent,但是不能设置为null + [MemoryPackIgnore] [BsonIgnore] public Entity Parent { @@ -290,6 +297,7 @@ public T GetParent() where T : Entity [BsonIgnore] protected IScene iScene; + [MemoryPackIgnore] [BsonIgnore] public IScene IScene { @@ -368,13 +376,15 @@ protected set } } + [MemoryPackInclude] [BsonElement("Children")] [BsonIgnoreIfNull] - private List childrenDB; + protected List childrenDB; [BsonIgnore] private SortedDictionary children; + [MemoryPackIgnore] [BsonIgnore] public SortedDictionary Children { @@ -405,13 +415,15 @@ private void RemoveFromChildren(Entity entity) } } + [MemoryPackInclude] [BsonElement("C")] [BsonIgnoreIfNull] - private List componentsDB; + protected List componentsDB; [BsonIgnore] private SortedDictionary components; + [MemoryPackIgnore] [BsonIgnore] public SortedDictionary Components { @@ -524,7 +536,10 @@ public override void Dispose() status = EntityStatus.None; - ObjectPool.Instance.Recycle(this); + if (this.IsFromPool) + { + ObjectPool.Instance.Recycle(this); + } } private void AddToComponents(Entity component) @@ -690,7 +705,17 @@ public Entity GetComponent(Type type) private static Entity Create(Type type, bool isFromPool) { - Entity component = (Entity) ObjectPool.Instance.Fetch(type, isFromPool); + Entity component; + if (isFromPool) + { + component = (Entity) ObjectPool.Instance.Fetch(type); + } + else + { + component = Activator.CreateInstance(type) as Entity; + } + + component.IsFromPool = isFromPool; component.IsCreated = true; component.IsNew = true; component.Id = 0; diff --git a/Unity/Assets/Scripts/Model/Share/EntitySerializeRegister.cs b/Unity/Assets/Scripts/Model/Share/EntitySerializeRegister.cs new file mode 100644 index 00000000000..76246d07302 --- /dev/null +++ b/Unity/Assets/Scripts/Model/Share/EntitySerializeRegister.cs @@ -0,0 +1,13 @@ +namespace ET +{ + public static partial class EntitySerializeRegister + { + static partial void Register(); + + public static void Init() + { + Register(); + } + } +} + diff --git a/Unity/Assets/Scripts/Model/Share/EntitySerializeRegister.cs.meta b/Unity/Assets/Scripts/Model/Share/EntitySerializeRegister.cs.meta new file mode 100644 index 00000000000..c0e72de503f --- /dev/null +++ b/Unity/Assets/Scripts/Model/Share/EntitySerializeRegister.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 78d722c2a1419e14d9a93a223242bd58 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity/Assets/Scripts/Model/Share/Entry.cs b/Unity/Assets/Scripts/Model/Share/Entry.cs index ecb5753114a..055214199c5 100644 --- a/Unity/Assets/Scripts/Model/Share/Entry.cs +++ b/Unity/Assets/Scripts/Model/Share/Entry.cs @@ -34,7 +34,8 @@ private static async ETTask StartAsync() // 注册Mongo type MongoRegister.Init(); - + // 注册Entity序列化器 + EntitySerializeRegister.Init(); World.Instance.AddSingleton(); World.Instance.AddSingleton(); World.Instance.AddSingleton(); diff --git a/Unity/Assets/Scripts/Model/Share/LockStep/LSUnit.cs b/Unity/Assets/Scripts/Model/Share/LockStep/LSUnit.cs index 33ff571bb25..7152412aa92 100644 --- a/Unity/Assets/Scripts/Model/Share/LockStep/LSUnit.cs +++ b/Unity/Assets/Scripts/Model/Share/LockStep/LSUnit.cs @@ -1,11 +1,13 @@ using System; +using MemoryPack; using MongoDB.Bson.Serialization.Attributes; using TrueSync; namespace ET { [ChildOf(typeof(LSUnitComponent))] - public class LSUnit: LSEntity, IAwake, ISerializeToEntity + [MemoryPackable] + public partial class LSUnit: LSEntity, IAwake, ISerializeToEntity { public TSVector Position { @@ -13,6 +15,7 @@ public TSVector Position set; } + [MemoryPackIgnore] [BsonIgnore] public TSVector Forward { diff --git a/Unity/Assets/Scripts/Model/Share/Module/Unit/Unit.cs b/Unity/Assets/Scripts/Model/Share/Module/Unit/Unit.cs index e66f8f51e1f..6e07849c48c 100644 --- a/Unity/Assets/Scripts/Model/Share/Module/Unit/Unit.cs +++ b/Unity/Assets/Scripts/Model/Share/Module/Unit/Unit.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using MemoryPack; using MongoDB.Bson.Serialization.Attributes; using Unity.Mathematics; @@ -6,13 +7,16 @@ namespace ET { [ChildOf(typeof(UnitComponent))] [DebuggerDisplay("ViewName,nq")] - public class Unit: Entity, IAwake + [MemoryPackable] + public partial class Unit: Entity, IAwake { public int ConfigId { get; set; } //配置表id + [MemoryPackInclude] [BsonElement] private float3 position; //坐标 + [MemoryPackIgnore] [BsonIgnore] public float3 Position { @@ -25,6 +29,7 @@ public float3 Position } } + [MemoryPackIgnore] [BsonIgnore] public float3 Forward { @@ -32,9 +37,11 @@ public float3 Forward set => this.Rotation = quaternion.LookRotation(value, math.up()); } + [MemoryPackInclude] [BsonElement] private quaternion rotation; + [MemoryPackIgnore] [BsonIgnore] public quaternion Rotation {