Entitas  0.35.0
Entitas is a super fast Entity Component System (ECS) Framework specifically made for C# and Unity
ComponentIndicesGenerator.cs
1 using System.Collections.Generic;
2 using System.Linq;
3 
4 namespace Entitas.CodeGenerator {
5 
7 
8  // Important: This method should be called before Generate(componentInfos)
9  // This will generate empty lookups for all pools.
10  public CodeGenFile[] Generate(string[] poolNames) {
11  var emptyInfos = new ComponentInfo[0];
12  var generatorName = GetType().FullName;
13  return poolNames
14  .Select(poolName => poolName.PoolPrefix() + CodeGenerator.DEFAULT_COMPONENT_LOOKUP_TAG)
15  .Select(lookupTag => new CodeGenFile(
16  lookupTag,
17  generateIndicesLookup(lookupTag, emptyInfos),
18  generatorName
19  )).ToArray();
20  }
21 
22  // Important: This method should be called after Generate(poolNames)
23  // This will overwrite the empty lookups with the actual content.
24  public CodeGenFile[] Generate(ComponentInfo[] componentInfos) {
25  var orderedComponentInfos = componentInfos.OrderBy(info => info.typeName).ToArray();
26  var lookupTagToComponentInfosMap = getLookupTagToComponentInfosMap(orderedComponentInfos);
27  var generatorName = GetType().FullName;
28  return lookupTagToComponentInfosMap
29  .Select(kv => new CodeGenFile(
30  kv.Key,
31  generateIndicesLookup(kv.Key, kv.Value.ToArray()),
32  generatorName
33  )).ToArray();
34  }
35 
36  static Dictionary<string, ComponentInfo[]> getLookupTagToComponentInfosMap(ComponentInfo[] componentInfos) {
37  var currentIndex = 0;
38 
39  // order componentInfos by pool count
40  var orderedComponentInfoToLookupTagsMap = componentInfos
41  .Where(info => info.generateIndex)
42  .ToDictionary(info => info, info => info.ComponentLookupTags())
43  .OrderByDescending(kv => kv.Value.Length);
44 
45  var lookupTagToComponentInfosMap = orderedComponentInfoToLookupTagsMap
46  .Aggregate(new Dictionary<string, ComponentInfo[]>(), (map, kv) => {
47  var info = kv.Key;
48  var lookupTags = kv.Value;
49  var componentIsAssignedToMultiplePools = lookupTags.Length > 1;
50  var incrementIndex = false;
51  foreach(var lookupTag in lookupTags) {
52  if(!map.ContainsKey(lookupTag)) {
53  map.Add(lookupTag, new ComponentInfo[componentInfos.Length]);
54  }
55 
56  var infos = map[lookupTag];
57  if(componentIsAssignedToMultiplePools) {
58  // Component has multiple lookupTags. Set at current index in all lookups.
59  infos[currentIndex] = info;
60  incrementIndex = true;
61  } else {
62  // Component has only one lookupTag. Insert at next free slot.
63  for(int i = 0; i < infos.Length; i++) {
64  if(infos[i] == null) {
65  infos[i] = info;
66  break;
67  }
68  }
69  }
70  }
71  if(incrementIndex) {
72  currentIndex++;
73  }
74 
75  return map;
76  });
77 
78 
79  foreach(var key in lookupTagToComponentInfosMap.Keys.ToArray()) {
80  var infoList = lookupTagToComponentInfosMap[key].ToList();
81  while(infoList.Count != 0) {
82  var last = infoList.Count - 1;
83  if(infoList[last] == null) {
84  infoList.RemoveAt(last);
85  } else {
86  break;
87  }
88  }
89  lookupTagToComponentInfosMap[key] = infoList.ToArray();
90  }
91 
92  return lookupTagToComponentInfosMap;
93  }
94 
95  static string generateIndicesLookup(string lookupTag, ComponentInfo[] componentInfos) {
96  return addClassHeader(lookupTag)
97  + addIndices(componentInfos)
98  + addComponentNames(componentInfos)
99  + addComponentTypes(componentInfos)
100  + addCloseClass();
101  }
102 
103  static string addClassHeader(string lookupTag) {
104  return string.Format("public static class {0} {{\n", lookupTag);
105  }
106 
107  static string addIndices(ComponentInfo[] componentInfos) {
108  const string fieldFormat = " public const int {0} = {1};\n";
109  const string totalFormat = " public const int TotalComponents = {0};";
110  var code = string.Empty;
111  for(int i = 0; i < componentInfos.Length; i++) {
112  var info = componentInfos[i];
113  if(info != null) {
114  code += string.Format(fieldFormat, info.typeName.RemoveComponentSuffix(), i);
115  }
116  }
117 
118  if(code.Length != 0) {
119  code = "\n" + code;
120  }
121 
122  var totalComponents = string.Format(totalFormat, componentInfos.Length);
123  return code + "\n" + totalComponents;
124  }
125 
126  static string addComponentNames(ComponentInfo[] componentInfos) {
127  const string format = " \"{1}\",\n";
128  const string nullFormat = " null,\n";
129  var code = string.Empty;
130  for(int i = 0; i < componentInfos.Length; i++) {
131  var info = componentInfos[i];
132  if(info != null) {
133  code += string.Format(format, i, info.typeName.RemoveComponentSuffix());
134  } else {
135  code += nullFormat;
136  }
137  }
138  if(code.EndsWith(",\n", System.StringComparison.Ordinal)) {
139  code = code.Remove(code.Length - 2) + "\n";
140  }
141 
142  return string.Format(@"
143 
144  public static readonly string[] componentNames = {{
145 {0} }};", code);
146  }
147 
148  static string addComponentTypes(ComponentInfo[] componentInfos) {
149  const string format = " typeof({1}),\n";
150  const string nullFormat = " null,\n";
151  var code = string.Empty;
152  for(int i = 0; i < componentInfos.Length; i++) {
153  var info = componentInfos[i];
154  if(info != null) {
155  code += string.Format(format, i, info.fullTypeName);
156  } else {
157  code += nullFormat;
158  }
159  }
160  if(code.EndsWith(",\n", System.StringComparison.Ordinal)) {
161  code = code.Remove(code.Length - 2) + "\n";
162  }
163 
164  return string.Format(@"
165 
166  public static readonly System.Type[] componentTypes = {{
167 {0} }};", code);
168  }
169 
170  static string addCloseClass() {
171  return "\n}\n";
172  }
173  }
174 }