using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Vintagestory.API.Client; using Vintagestory.API.Common; using Vintagestory.API.Config; using Vintagestory.API.Datastructures; using Vintagestory.API.MathTools; using Vintagestory.Common; namespace Automap { public class JsonGenerator { private ICoreClientAPI ClientAPI { get; set; } private ILogger Logger { get; set; } private string Path { get; set; } private readonly int chunkSize; internal JArray ColumnMeta_Names, PointsOfInterest_Names, EntitiesOfInterest_Names; internal MemberInfo[] ColumnMeta_Vals, PointsOfInterest_Vals, EntitiesOfInterest_Vals; public JsonGenerator(ICoreClientAPI _ClientAPI, ILogger _Logger, string _path) { this.ClientAPI = _ClientAPI; this.Logger = _Logger; this.Path = _path; this.chunkSize = ClientAPI.World.BlockAccessor.ChunkSize; ColumnMeta_Names = Dynamic_Names(); ColumnMeta_Vals = Dynamic_Values(); PointsOfInterest_Names = Dynamic_Names(); PointsOfInterest_Vals = Dynamic_Values(); EntitiesOfInterest_Names = Dynamic_Names(); EntitiesOfInterest_Vals = Dynamic_Values(); Logger.VerboseDebug("JSON Ready"); } /// /// Generates the JSON Metadata. (in Map object format ) /// public void GenerateJSONMetadata(ColumnsMetadata chunkTopMetadata, Vec2i startChunkColumn, PointsOfInterest POIs, EntitiesOfInterest EOIs, Dictionary RockIdCodes) { //Console.WriteLine($"53 {chunkTopMetadata.Count}"); string jsonFilename = System.IO.Path.Combine(Path, "Metadata.js"); StreamWriter stream = new StreamWriter(jsonFilename, false, Encoding.UTF8); using (stream) { JsonTextWriter jsonWriter = new JsonTextWriter(stream); jsonWriter.Formatting = Formatting.None; jsonWriter.StringEscapeHandling = StringEscapeHandling.EscapeHtml; jsonWriter.Indentation = 0; //jsonWriter.AutoCompleteOnClose = true; jsonWriter.QuoteChar = '\''; jsonWriter.DateFormatHandling = DateFormatHandling.IsoDateFormat; jsonWriter.DateTimeZoneHandling = DateTimeZoneHandling.Utc; using (jsonWriter) { jsonWriter.WriteRaw("ViewFrame.chunks="); jsonWriter.WriteStartObject(); jsonWriter.WriteKeyValue("worldSeedNum", ClientAPI.World.Seed); jsonWriter.WriteKeyValue("genTime", DateTimeOffset.UtcNow); jsonWriter.WriteKeyRawValue("startCoords", $"[{startChunkColumn.X},{startChunkColumn.Y}]"); jsonWriter.WriteKeyValue("chunkSize", chunkSize); jsonWriter.WriteArray("edges", new int[]{ chunkTopMetadata.North_mostChunk, chunkTopMetadata.East_mostChunk, chunkTopMetadata.South_mostChunk, chunkTopMetadata.West_mostChunk }); jsonWriter.WriteArray("chunkMetadataNames", ColumnMeta_Names); jsonWriter.WriteArray("pointsOfInterestNames", PointsOfInterest_Names); jsonWriter.WriteArray("entityOfInterestNames", EntitiesOfInterest_Names); //MAP object format - [key, value]: key is "x_y" jsonWriter.WriteMapForeach("chunkMetadata", chunkTopMetadata, (shard) => { //Console.WriteLine($"188 {chunkTopMetadata.Count}"); jsonWriter.WriteMapTupleForeach( $"{shard.Location.X}_{shard.Location.Y}", ColumnMeta_Vals, (mem) => { var val = GetValue(mem, shard); if (IsFloatingType(val)) jsonWriter.WriteValue($"{val:F3}"); else if (IsIntegralType(val)) jsonWriter.WriteValue($"{val}"); else jsonWriter.WriteValue(val); } ); }); #if DEBUG jsonWriter.WriteWhitespace("\n"); #endif jsonWriter.WriteMapForeach("pointsOfInterest", POIs, (poi) => { jsonWriter.WriteMapTupleForeach( $"{poi.Location.X}_{poi.Location.Z}", PointsOfInterest_Vals, (mem) => { var dasField = GetValue(mem, poi); jsonWriter.WriteRawValue(JsonConvert.SerializeObject(dasField)); } ); }); #if DEBUG jsonWriter.WriteWhitespace("\n"); #endif jsonWriter.WriteMapForeach("entityOfInterest", EOIs, (poi) => { jsonWriter.WriteMapTupleForeach( $"{poi.Location.X}_{poi.Location.Z}", EntitiesOfInterest_Vals, (el) => jsonWriter.WriteValue(GetValue(el, poi)) ); }); #if DEBUG jsonWriter.WriteWhitespace("\n"); #endif jsonWriter.WriteWhitespace("\n"); jsonWriter.WriteComment("============= BlockID's for Rockmap / Rock-ratios ==============="); jsonWriter.WriteWhitespace("\n"); jsonWriter.WriteMapForeach("rockLookup", RockIdCodes, (rock) => { var block = ClientAPI.World.GetBlock(rock.Key); jsonWriter.WriteMapTuple(rock.Key.ToString(), Lang.GetUnformatted(block.Code.Path)); }); jsonWriter.WriteEndObject(); jsonWriter.WriteRaw(";"); jsonWriter.Flush(); } } } internal JArray Dynamic_Names() where TData : struct { Dictionary memberNames = new Dictionary(); foreach (var memberInfo in typeof(TData).GetMembers(BindingFlags.Instance | BindingFlags.Public)) { DisplayNameAttribute displayName = memberInfo.GetCustomAttribute(); if (displayName != null) { if (!memberNames.ContainsKey(displayName.order)) {//No duplicates, no overwrites memberNames.Add(displayName.order, displayName.name); } } } return new JArray(memberNames.OrderBy(kf => kf.Key).Select(kf => kf.Value).ToArray()); } internal MemberInfo[] Dynamic_Values() where TData : struct { Dictionary memberVals = new Dictionary(); foreach (var memberInfo in typeof(TData).GetMembers(BindingFlags.Instance | BindingFlags.Public)) { DisplayNameAttribute displayName = memberInfo.GetCustomAttribute(); if (displayName != null && !memberVals.ContainsKey(displayName.order)) { memberVals.Add(displayName.order, memberInfo); } } return memberVals.OrderBy(kf => kf.Key).Select(kf => kf.Value).ToArray(); } internal static object GetValue(MemberInfo member, object property) { // copied from https://stackoverflow.com/questions/12680341/how-to-get-both-fields-and-properties-in-single-call-via-reflection if (member.MemberType == MemberTypes.Property) return ((PropertyInfo) member).GetValue(property, null); else if (member.MemberType == MemberTypes.Field) return ((FieldInfo) member).GetValue(property); else throw new Exception("Property must be of type FieldInfo or PropertyInfo"); } internal static bool IsFloatingType(object o) { if (o == null) return false; switch (Type.GetTypeCode(o.GetType())) { case TypeCode.Decimal: case TypeCode.Double: case TypeCode.Single: return true; default: return false; } } internal static bool IsIntegralType(object o) { if (o == null) return false; switch (Type.GetTypeCode(o.GetType())) { case TypeCode.Byte: case TypeCode.SByte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: return true; default: return false; } } } public static class JsonWriterExtentions { /// /// Writes an array in the form of key: [...ar] by calling .ToString() on all elements in ar. /// /// /// /// public static void WriteArray(this JsonTextWriter writer, string key, T[] ar) { writer.WritePropertyName(key); writer.WriteStartArray(); foreach (var el in ar) { writer.WriteValue(el); } writer.WriteEndArray(); } /// /// Like WriteArray(string, object[]) but for JArrays. /// /// /// /// public static void WriteArray(this JsonTextWriter writer, string key, JArray ar) { writer.WritePropertyName(key); ar.WriteTo(writer); } /// /// Easy extention to write JArrays. /// /// /// public static void WriteValue(this JsonTextWriter writer, JArray ar) { ar.WriteTo(writer); } public static void WriteKeyValue(this JsonTextWriter writer, string key, object value) { writer.WritePropertyName(key); writer.WriteValue(value); } public static void WriteKeyRawValue(this JsonTextWriter writer, string key, string value) { writer.WritePropertyName(key); writer.WriteRawValue(value); } /// /// Writes a Map by calling middle in the middle of the Map values array. /// /// /// /// public static void WriteMap(this JsonTextWriter writer, string key, System.Action middle) { writer.WritePropertyName(key); writer.WriteStartConstructor("Map"); writer.WriteStartArray(); middle(); writer.WriteEndArray(); writer.WriteEndConstructor(); } /// /// Writes a Map by calling middle on every element in en. /// /// /// /// /// /// public static void WriteMapForeach(this JsonTextWriter writer, string key, IEnumerable en, System.Action middle) { writer.WritePropertyName(key); writer.WriteStartConstructor("Map"); writer.WriteStartArray(); foreach (var el in en) { middle(el); } writer.WriteEndArray(); writer.WriteEndConstructor(); } /// /// Writes a Map tuple in the form of [key, value]. /// /// /// /// public static void WriteMapTuple(this JsonTextWriter writer, string key, string value) { writer.WriteStartArray(); writer.WriteValue(key); writer.WriteValue(value); writer.WriteEndArray(); } /// /// Writes a Map tuple in the form of [key, [val, val]]. Where each val is given by the passed function. /// /// /// /// public static void WriteMapTupleForeach(this JsonTextWriter writer, string key, IEnumerable en, System.Action middle) { writer.WriteStartArray(); // start tuple writer.WriteValue(key); writer.WriteStartArray(); // start val foreach (var el in en) { middle(el); } writer.WriteEndArray(); // end val writer.WriteEndArray(); // end tuple } } }