OSDN Git Service

Revisit(er) & ReadOnly chunk use
[automap/automap.git] / Automap / Subsystems / JsonGenerator.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Globalization;
4 using System.IO;
5 using System.Linq;
6 using System.Reflection;
7 using System.Text;
8
9 using Newtonsoft.Json;
10 using Newtonsoft.Json.Linq;
11
12 using Vintagestory.API.Client;
13 using Vintagestory.API.Common;
14 using Vintagestory.API.Config;
15 using Vintagestory.API.Datastructures;
16 using Vintagestory.API.MathTools;
17 using Vintagestory.Common;
18
19
20 namespace Automap
21 {
22         public class JsonGenerator
23         {
24                 private ICoreClientAPI ClientAPI { get; set; }
25                 private ILogger Logger { get; set; }
26                 private string Path { get; set; }
27                 private readonly int chunkSize;
28
29                 internal JArray ColumnMeta_Names, PointsOfInterest_Names, EntitiesOfInterest_Names;
30                 internal MemberInfo[] ColumnMeta_Vals, PointsOfInterest_Vals, EntitiesOfInterest_Vals;
31
32                 public JsonGenerator(ICoreClientAPI _ClientAPI, ILogger _Logger, string _path)
33                 {
34                         this.ClientAPI = _ClientAPI;
35                         this.Logger = _Logger;
36                         this.Path = _path;
37                         this.chunkSize = ClientAPI.World.BlockAccessor.ChunkSize;
38
39                         ColumnMeta_Names = Dynamic_Names<ColumnMeta>();
40                         ColumnMeta_Vals = Dynamic_Values<ColumnMeta>();
41                         PointsOfInterest_Names = Dynamic_Names<PointOfInterest>();
42                         PointsOfInterest_Vals = Dynamic_Values<PointOfInterest>();
43                         EntitiesOfInterest_Names = Dynamic_Names<EntityOfInterest>();
44                         EntitiesOfInterest_Vals = Dynamic_Values<EntityOfInterest>();
45                         Logger.VerboseDebug("JSON Ready");
46                 }
47
48
49                 /// <summary>
50                 /// Generates the JSON Metadata. (in Map object format )
51                 /// </summary>
52                 public void GenerateJSONMetadata(ColumnsMetadata chunkTopMetadata, Vec2i startChunkColumn, PointsOfInterest POIs, EntitiesOfInterest EOIs, Dictionary<int, string> RockIdCodes)
53                 {
54                         //Console.WriteLine($"53 {chunkTopMetadata.Count}");
55                         string jsonFilename = System.IO.Path.Combine(Path, "Metadata.js");
56
57                         StreamWriter stream = new StreamWriter(jsonFilename, false, Encoding.UTF8);
58
59                         using (stream)
60                         {
61                                 JsonTextWriter jsonWriter = new JsonTextWriter(stream);
62
63                                 jsonWriter.Formatting = Formatting.None;
64                                 jsonWriter.StringEscapeHandling = StringEscapeHandling.EscapeHtml;
65                                 jsonWriter.Indentation = 0;
66                                 //jsonWriter.AutoCompleteOnClose = true;
67                                 jsonWriter.QuoteChar = '\'';
68                                 jsonWriter.DateFormatHandling = DateFormatHandling.IsoDateFormat;
69                                 jsonWriter.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
70
71                                 using (jsonWriter)
72                                 {
73                                         jsonWriter.WriteRaw("ViewFrame.chunks=");
74                                         jsonWriter.WriteStartObject();
75                                         jsonWriter.WriteKeyValue("worldSeedNum", ClientAPI.World.Seed);
76
77                                         jsonWriter.WriteKeyValue("genTime", DateTimeOffset.UtcNow);
78
79                                         jsonWriter.WriteKeyRawValue("startCoords", $"[{startChunkColumn.X},{startChunkColumn.Y}]");
80                                         jsonWriter.WriteKeyRawValue("defaultSpawnPos", $"[{ClientAPI.World.DefaultSpawnPosition.AsBlockPos.X},{ClientAPI.World.DefaultSpawnPosition.AsBlockPos.Y},{ClientAPI.World.DefaultSpawnPosition.AsBlockPos.Z}]" );
81                                         jsonWriter.WriteKeyValue("chunkSize", chunkSize);
82
83                                         jsonWriter.WriteArray("edges", new int[]{
84                                         chunkTopMetadata.North_mostChunk,
85                                         chunkTopMetadata.East_mostChunk,
86                                         chunkTopMetadata.South_mostChunk,
87                                         chunkTopMetadata.West_mostChunk });
88
89                                         jsonWriter.WriteArray("chunkMetadataNames", ColumnMeta_Names);
90
91                                         jsonWriter.WriteArray("pointsOfInterestNames", PointsOfInterest_Names);
92
93                                         jsonWriter.WriteArray("entityOfInterestNames", EntitiesOfInterest_Names);
94
95                                         //MAP object format - [key, value]: key is "x_y"
96                                         jsonWriter.WriteMapForeach("chunkMetadata", chunkTopMetadata, (shard) =>
97                                         {
98                                                 //Console.WriteLine($"188 {chunkTopMetadata.Count}");
99                                                 jsonWriter.WriteMapTupleForeach(
100                                                         $"{shard.Location.X}_{shard.Location.Y}",
101                                                         ColumnMeta_Vals,
102                                                         (mem) =>
103                                                         {
104                                                                 var val = GetValue(mem, shard);
105                                                                 if (IsFloatingType(val))
106                                                                         jsonWriter.WriteValue($"{val:F3}");
107                                                                 else if (IsIntegralType(val))
108                                                                         jsonWriter.WriteValue($"{val}");
109                                                                 else
110                                                                         jsonWriter.WriteValue(val);
111                                                         }
112                                                 );
113                                         });
114 #if DEBUG
115                                         jsonWriter.WriteWhitespace("\n");
116 #endif
117                                         jsonWriter.WriteMapForeach("pointsOfInterest", POIs, (poi) =>
118                                         {
119                                                 jsonWriter.WriteMapTupleForeach(
120                                                         $"{poi.Location.X}_{poi.Location.Z}",
121                                                         PointsOfInterest_Vals,
122                                                         (mem) => {
123                                                         var dasField = GetValue(mem, poi);
124                                                         if (Type.GetTypeCode(mem.GetType( )) == TypeCode.String) {
125                                                                         string dasString = dasField as string;
126                                                                         jsonWriter.WriteValue(dasString.Replace('\'', ' '));
127                                                         }
128                                                         else { jsonWriter.WriteRawValue(JsonConvert.SerializeObject(dasField)); }
129                                                         }
130                                                 );
131                                         });
132 #if DEBUG
133                                         jsonWriter.WriteWhitespace("\n");
134 #endif
135                                         jsonWriter.WriteMapForeach("entityOfInterest", EOIs, (poi) =>
136                                         {
137                                                 jsonWriter.WriteMapTupleForeach(
138                                                         $"{poi.Location.X}_{poi.Location.Z}",
139                                                         EntitiesOfInterest_Vals,
140                                                         (el) => jsonWriter.WriteValue(GetValue(el, poi))
141                                                 );
142                                         });
143 #if DEBUG
144                                         jsonWriter.WriteWhitespace("\n");
145 #endif
146                                         jsonWriter.WriteWhitespace("\n");
147                                         jsonWriter.WriteComment("============= BlockID's for Rockmap / Rock-ratios ===============");
148                                         jsonWriter.WriteWhitespace("\n");
149
150                                         jsonWriter.WriteMapForeach("rockLookup", RockIdCodes, (rock) =>
151                                         {
152                                                 var block = ClientAPI.World.GetBlock(rock.Key);
153                                                 jsonWriter.WriteMapTuple(rock.Key.ToString(), Lang.GetUnformatted(block.Code.Path));
154                                         });
155                                         jsonWriter.WriteEndObject();
156                                         jsonWriter.WriteRaw(";");
157
158                                         jsonWriter.Flush();
159                                 }
160                         }
161
162                 }
163
164
165                 internal JArray Dynamic_Names<TData>() where TData : struct
166                 {
167                         Dictionary<byte, string> memberNames = new Dictionary<byte, string>();
168
169                         foreach (var memberInfo in typeof(TData).GetMembers(BindingFlags.Instance | BindingFlags.Public))
170                         {
171                                 DisplayNameAttribute displayName = memberInfo.GetCustomAttribute<DisplayNameAttribute>();
172                                 if (displayName != null)
173                                 {
174                                         if (!memberNames.ContainsKey(displayName.order))
175                                         {//No duplicates, no overwrites
176                                                 memberNames.Add(displayName.order, displayName.name);
177                                         }
178                                 }
179                         }
180
181                         return new JArray(memberNames.OrderBy(kf => kf.Key).Select(kf => kf.Value).ToArray());
182                 }
183
184                 internal MemberInfo[] Dynamic_Values<TData>() where TData : struct
185                 {
186                         Dictionary<byte, MemberInfo> memberVals = new Dictionary<byte, MemberInfo>();
187
188                         foreach (var memberInfo in typeof(TData).GetMembers(BindingFlags.Instance | BindingFlags.Public))
189                         {
190                                 DisplayNameAttribute displayName = memberInfo.GetCustomAttribute<DisplayNameAttribute>();
191                                 if (displayName != null && !memberVals.ContainsKey(displayName.order))
192                                 {
193                                         memberVals.Add(displayName.order, memberInfo);
194                                 }
195                         }
196
197                         return memberVals.OrderBy(kf => kf.Key).Select(kf => kf.Value).ToArray();
198                 }
199
200                 internal static object GetValue(MemberInfo member, object property)
201                 { // copied from https://stackoverflow.com/questions/12680341/how-to-get-both-fields-and-properties-in-single-call-via-reflection
202                         if (member.MemberType == MemberTypes.Property)
203                                 return ((PropertyInfo) member).GetValue(property, null);
204                         else if (member.MemberType == MemberTypes.Field)
205                                 return ((FieldInfo) member).GetValue(property);
206                         else
207                                 throw new Exception("Property must be of type FieldInfo or PropertyInfo");
208                 }
209
210                 internal static bool IsFloatingType(object o)
211                 {
212                         if (o == null) return false;
213                         switch (Type.GetTypeCode(o.GetType()))
214                         {
215                                 case TypeCode.Decimal:
216                                 case TypeCode.Double:
217                                 case TypeCode.Single:
218                                         return true;
219                                 default:
220                                         return false;
221                         }
222                 }
223
224                 internal static bool IsIntegralType(object o)
225                 {
226                         if (o == null) return false;
227                         switch (Type.GetTypeCode(o.GetType()))
228                         {
229                                 case TypeCode.Byte:
230                                 case TypeCode.SByte:
231                                 case TypeCode.UInt16:
232                                 case TypeCode.UInt32:
233                                 case TypeCode.UInt64:
234                                 case TypeCode.Int16:
235                                 case TypeCode.Int32:
236                                 case TypeCode.Int64:
237                                         return true;
238                                 default:
239                                         return false;
240                         }
241                 }
242         }
243
244         public static class JsonWriterExtentions
245         {
246                 
247                 /// <summary>
248                 /// Writes an array in the form of key: [...ar] by calling .ToString() on all elements in ar.
249                 /// </summary>
250                 /// <param name="writer"></param>
251                 /// <param name="key"></param>
252                 /// <param name="ar"></param>
253                 public static void WriteArray<T>(this JsonTextWriter writer, string key, T[] ar)
254                 {
255                         writer.WritePropertyName(key);
256                         writer.WriteStartArray();
257                         foreach (var el in ar)
258                         {
259                                 writer.WriteValue(el);
260                         }
261                         writer.WriteEndArray();
262                 }
263
264
265                 /// <summary>
266                 /// Like WriteArray(string, object[]) but for JArrays.
267                 /// </summary>
268                 /// <param name="writer"></param>
269                 /// <param name="key"></param>
270                 /// <param name="ar"></param>
271                 public static void WriteArray(this JsonTextWriter writer, string key, JArray ar)
272                 {
273                         writer.WritePropertyName(key);
274                         ar.WriteTo(writer);
275                 }
276
277                 /// <summary>
278                 /// Easy extention to write JArrays.
279                 /// </summary>
280                 /// <param name="writer"></param>
281                 /// <param name="ar"></param>
282                 public static void WriteValue(this JsonTextWriter writer, JArray ar)
283                 {
284                         ar.WriteTo(writer);
285                 }
286
287                 public static void WriteKeyValue(this JsonTextWriter writer, string key, object value)
288                 {
289                         writer.WritePropertyName(key);
290                         writer.WriteValue(value);
291                 }
292
293                 public static void WriteKeyRawValue(this JsonTextWriter writer, string key, string value)
294                 {
295                         writer.WritePropertyName(key);
296                         writer.WriteRawValue(value);
297                 }
298
299                 /// <summary>
300                 /// Writes a Map by calling middle in the middle of the Map values array.
301                 /// </summary>
302                 /// <param name="writer"></param>
303                 /// <param name="key"></param>
304                 /// <param name="middle"></param>
305                 public static void WriteMap(this JsonTextWriter writer, string key, System.Action middle)
306                 {
307                         writer.WritePropertyName(key);
308                         writer.WriteStartConstructor("Map");
309                         writer.WriteStartArray();
310                         middle();
311                         writer.WriteEndArray();
312                         writer.WriteEndConstructor();
313                 }
314
315                 /// <summary>
316                 /// Writes a Map by calling middle on every element in en.
317                 /// </summary>
318                 /// <typeparam name="T"></typeparam>
319                 /// <param name="writer"></param>
320                 /// <param name="key"></param>
321                 /// <param name="en"></param>
322                 /// <param name="middle"></param>
323                 public static void WriteMapForeach<T>(this JsonTextWriter writer, string key, IEnumerable<T> en, System.Action<T> middle)
324                 {
325                         writer.WritePropertyName(key);
326                         writer.WriteStartConstructor("Map");
327                         writer.WriteStartArray();
328                         foreach (var el in en)
329                         {
330                                 middle(el);
331                         }
332                         writer.WriteEndArray();
333                         writer.WriteEndConstructor();
334                 }
335
336                 /// <summary>
337                 /// Writes a Map tuple in the form of [key, value].
338                 /// </summary>
339                 /// <param name="writer"></param>
340                 /// <param name="key"></param>
341                 /// <param name="value"></param>
342                 public static void WriteMapTuple(this JsonTextWriter writer, string key, string value)
343                 {
344                         writer.WriteStartArray();
345                         writer.WriteValue(key);
346                         writer.WriteValue(value);
347                         writer.WriteEndArray();
348                 }
349
350                 /// <summary>
351                 /// Writes a Map tuple in the form of [key, [val, val]]. Where each val is given by the passed function.
352                 /// </summary>
353                 /// <param name="writer"></param>
354                 /// <param name="key"></param>
355                 /// <param name="middle"></param>
356                 public static void WriteMapTupleForeach<T>(this JsonTextWriter writer, string key, IEnumerable<T> en, System.Action<T> middle)
357                 {
358                         writer.WriteStartArray(); // start tuple
359                         writer.WriteValue(key);
360                         writer.WriteStartArray(); // start val
361                         foreach (var el in en)
362                         {
363                                 middle(el);
364                         }
365                         writer.WriteEndArray(); // end val
366                         writer.WriteEndArray(); // end tuple
367                 }
368         }
369 }
370