OSDN Git Service

Pre-RC0: Pass 2; JSON Field metadata dynamic generation.
[automap/automap.git] / Automap / Subsystems / AutomapSystem.cs
index 28b40cb..8cbfc20 100644 (file)
@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using System.Reflection;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Threading;
@@ -12,6 +13,8 @@ using Hjg.Pngcs.Chunks;
 
 using Newtonsoft.Json;
 
+using ProtoBuf;
+
 using Vintagestory.API.Client;
 using Vintagestory.API.Common;
 using Vintagestory.API.Config;
@@ -27,17 +30,22 @@ namespace Automap
                private ICoreClientAPI ClientAPI { get; set; }
                private ILogger Logger { get; set; }
                private IChunkRenderer ChunkRenderer { get; set; }
+               private JsonGenerator JsonGenerator { get; set; }
 
                private const string _mapPath = @"Maps";
                private const string _chunkPath = @"Chunks";
                private const string _domain = @"automap";
                private const string chunkFile_filter = @"*_*.png";
+               private const string poiFileName = @"poi_binary";
+               private const string eoiFileName = @"eoi_binary";
+               private const string pointsTsvFileName = @"points_of_interest.tsv";
                private static Regex chunkShardRegex = new Regex(@"(?<X>[\d]+)_(?<Z>[\d]+).png", RegexOptions.Singleline);
 
                private ConcurrentDictionary<Vec2i, uint> columnCounter = new ConcurrentDictionary<Vec2i, uint>(3, 150);
                private ColumnsMetadata chunkTopMetadata;
                private PointsOfInterest POIs = new PointsOfInterest();
                private EntitiesOfInterest EOIs = new EntitiesOfInterest();
+               private string jsonPreBuilt;
 
                internal Dictionary<int, BlockDesignator> BlockID_Designators { get; private set; }
                internal Dictionary<AssetLocation, EntityDesignator> Entity_Designators { get; private set; }
@@ -67,14 +75,13 @@ namespace Automap
                        configuration = config;
 
 
-
                        //TODO:Choose which one from GUI 
                        this.ChunkRenderer = new StandardRenderer(clientAPI, logger);
 
                        //Listen on bus for commands
                        ClientAPI.Event.RegisterEventBusListener(CommandListener, 1.0, AutomapSystem.AutomapCommandEventKey);
 
-                       if (configuration.Autostart) 
+                       if (configuration.Autostart)
                        {
                                CurrentState = CommandType.Run;
                                Logger.Debug("Autostart is Enabled.");
@@ -88,7 +95,7 @@ namespace Automap
                {
                        path = ClientAPI.GetOrCreateDataPath(_mapPath);
                        path = ClientAPI.GetOrCreateDataPath(Path.Combine(path, "World_" + ClientAPI.World.Seed));//Add name of World too...'ServerApi.WorldManager.CurrentWorldName'
-
+                       JsonGenerator = new JsonGenerator(ClientAPI, Logger, path);
 
                        string mapFilename = Path.Combine(path, "automap.html");
                        StreamWriter outputText = new StreamWriter(File.Open(mapFilename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite));
@@ -128,9 +135,9 @@ namespace Automap
 
                        if (CurrentState == CommandType.Run && (ClientAPI.IsGamePaused != false || ClientAPI.IsShuttingDown != true))
                        {
-#if DEBUG
+                               #if DEBUG
                                Logger.VerboseDebug("Cartographer re-trigger from [{0}]", cartographer_thread.ThreadState);
-#endif
+                               #endif
 
                                if (cartographer_thread.ThreadState.HasFlag(ThreadState.Unstarted))
                                {
@@ -168,7 +175,7 @@ namespace Automap
                                if (!columnCounter.IsEmpty)
                                {
                                        var tempSet = columnCounter.ToArray().OrderByDescending(kvp => kvp.Value);
-                                       UpdateEntityMetadata( );
+                                       UpdateEntityMetadata();
 
                                        foreach (var mostActiveCol in tempSet)
                                        {
@@ -183,11 +190,13 @@ namespace Automap
                                                }
 
                                                ColumnMeta chunkMeta;
-                                               if (chunkTopMetadata.Contains(mostActiveCol.Key)) {
-                                               chunkMeta = chunkTopMetadata[mostActiveCol.Key];
+                                               if (chunkTopMetadata.Contains(mostActiveCol.Key))
+                                               {
+                                                       chunkMeta = chunkTopMetadata[mostActiveCol.Key];
                                                }
-                                               else {
-                                               chunkMeta = CreateColumnMetadata(mostActiveCol, mapChunk);
+                                               else
+                                               {
+                                                       chunkMeta = CreateColumnMetadata(mostActiveCol, mapChunk);
                                                }
 
                                                ProcessChunkBlocks(mostActiveCol.Key, mapChunk, ref chunkMeta);
@@ -220,7 +229,7 @@ namespace Automap
                                {
                                        //What about chunk updates themselves; a update bitmap isn't kept...
                                        updatedChunksTotal += updatedChunks;
-                                       GenerateJSONMetadata();
+                                       JsonGenerator.GenerateJSONMetadata(chunkTopMetadata, startChunkColumn, POIs, EOIs, RockIdCodes);
                                        updatedChunks = 0;
                                }
 
@@ -246,6 +255,7 @@ namespace Automap
                        finally
                        {
                                Logger.VerboseDebug("Thread '{0}' executing finally block.", Thread.CurrentThread.Name);
+                               PersistPointsData( );
                        }
                }
 
@@ -269,7 +279,7 @@ namespace Automap
                }
 
                private void Reload_POI_Designators()
-               {                       
+               {
                        Logger.VerboseDebug("Connecting {0} Configured Block-Designators", configuration.BlockDesignators.Count);
                        foreach (var designator in configuration.BlockDesignators)
                        {
@@ -303,223 +313,59 @@ namespace Automap
 
                }
 
-               //TODO: Rewrite as Newtonsoft  JsonTextWriter !!!
+
+
                /// <summary>
-               /// Generates the JSON Metadata. (in Map object format )
+               /// Store Points/Entity of Interest
                /// </summary>
-               private void GenerateJSONMetadata()
+               private void PersistPointsData( )
                {
-                       string jsonFilename = 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={};\n");
-                               jsonWriter.WriteRaw("ViewFrame.chunks.worldSeedNum=" );
-                               jsonWriter.WriteValue(ClientAPI.World.Seed);
-                               jsonWriter.WriteRaw(";\n");
-
-                               jsonWriter.WriteRaw("ViewFrame.chunks.genTime=");
-                               jsonWriter.WriteValue(DateTimeOffset.UtcNow);
-                               jsonWriter.WriteRaw(";\n");
-
-                               jsonWriter.WriteRaw("ViewFrame.chunks.startCoords=");
-                               jsonWriter.WriteStartArray( );
-                               jsonWriter.WriteValue(startChunkColumn.X);
-                               jsonWriter.WriteValue(startChunkColumn.Y);
-                               jsonWriter.WriteEndArray( );
-                               jsonWriter.WriteRaw(";\n");
-
-                               jsonWriter.WriteRaw("ViewFrame.chunks.chunkSize=");
-                               jsonWriter.WriteValue(chunkSize);
-                               jsonWriter.WriteRaw(";\n");
-
-                               jsonWriter.WriteRaw("ViewFrame.chunks.northMostChunk=");
-                               jsonWriter.WriteValue(chunkTopMetadata.North_mostChunk);
-                               jsonWriter.WriteRaw(";\n");
-
-                               jsonWriter.WriteRaw("ViewFrame.chunks.southMostChunk=");
-                               jsonWriter.WriteValue(chunkTopMetadata.South_mostChunk);
-                               jsonWriter.WriteRaw(";\n");
-
-                               jsonWriter.WriteRaw("ViewFrame.chunks.westMostChunk=");
-                               jsonWriter.WriteValue(chunkTopMetadata.West_mostChunk);
-                               jsonWriter.WriteRaw(";\n");
-
-                               jsonWriter.WriteRaw("ViewFrame.chunks.eastMostChunk=");
-                               jsonWriter.WriteValue(chunkTopMetadata.East_mostChunk);
-                               jsonWriter.WriteRaw(";\n");
-
-
-                               //MAP object format - [key, value]: key is "x_y"
-                               jsonWriter.WriteRaw("ViewFrame.chunks.chunkMetadata=");
-                               jsonWriter.WriteStartConstructor("Map");
-                               jsonWriter.WriteStartArray( );//An array of... 2-component arrays
-
-
-                               foreach (var shard in chunkTopMetadata)
-                               {
-                                       jsonWriter.WriteStartArray( );//Start tuple
-                                       jsonWriter.WriteValue($"{shard.Location.X}_{shard.Location.Y}");//Key of Tuple
-
-                                       jsonWriter.WriteStartObject( );
-                                       jsonWriter.WritePropertyName("prettyCoord");
-                                       jsonWriter.WriteValue( shard.Location.PrettyCoords(ClientAPI));
-
-                                       jsonWriter.WritePropertyName("chunkAge");
-                                       jsonWriter.WriteValue(shard.ChunkAge);
-
-                                       jsonWriter.WritePropertyName("temp");
-                                       jsonWriter.WriteValue(shard.Temperature);
-
-                                       jsonWriter.WritePropertyName("YMax");
-                                       jsonWriter.WriteValue(shard.YMax);
-
-                                       jsonWriter.WritePropertyName("fert");
-                                       jsonWriter.WriteValue(shard.Fertility);
-                                       
-                                       jsonWriter.WritePropertyName("forestDens");
-                       jsonWriter.WriteValue( shard.ForestDensity);
-
-                                       jsonWriter.WritePropertyName("rain"); 
-                       jsonWriter.WriteValue( shard.Rainfall);
-
-                                       jsonWriter.WritePropertyName("shrubDens");
-                       jsonWriter.WriteValue(  shard.ShrubDensity);
-
-                                       jsonWriter.WritePropertyName("airBlocks");
-                       jsonWriter.WriteValue( shard.AirBlocks);
-
-                                       jsonWriter.WritePropertyName("nonAirBlocks");
-                       jsonWriter.WriteValue(  shard.NonAirBlocks);
-
-                                       //TODO: Heightmap ?
-                                       //Start rockMap ; FOR a Ratio....on tooltip GUI
-                                       jsonWriter.WritePropertyName("rockRatio");
-                                       jsonWriter.WriteStartConstructor("Map");
-                                       jsonWriter.WriteStartArray( );
-                                       foreach (var rockEntry in shard.RockRatio) {
-                                               var rockBlock = ClientAPI.World.GetBlock(rockEntry.Key);
-                                               jsonWriter.WriteStartArray( );
-                                               jsonWriter.WriteValue(rockBlock.Code.Path);
-                                               jsonWriter.WriteValue(rockEntry.Value);//Total per chunk-column
-                                               jsonWriter.WriteEndArray( );
-                                       }
-                                       jsonWriter.WriteEndArray( );
-                                       jsonWriter.WriteEndConstructor( );//end rock-map
-
-                                       jsonWriter.WriteEndObject( );//end Map value: {Object}
-                                       jsonWriter.WriteEndArray( );//end Tuple
-                               }
-
-                               jsonWriter.WriteEndArray( );//Enclose tuples of chunkMetadata
-                               jsonWriter.WriteEndConstructor( );//Close constructor of Map (chunkMetadata)
-                               jsonWriter.WriteRaw(";\n");
-
-                               jsonWriter.WriteRaw("ViewFrame.chunks.pointsOfInterest=");
-                               jsonWriter.WriteStartConstructor("Map");
-                               jsonWriter.WriteStartArray( );//An array of... 2-component arrays
-
-                               foreach (var poi in POIs)
-                               {
-                                       jsonWriter.WriteStartArray( );
-                                       jsonWriter.WriteValue($"{poi.Location.X}_{poi.Location.Z}");
-
-                                       jsonWriter.WriteStartObject();
-                                       jsonWriter.WritePropertyName("prettyCoord");
-                                       jsonWriter.WriteValue(poi.Location.PrettyCoords(ClientAPI) );
-
-                                       jsonWriter.WritePropertyName("notes");
-                                       jsonWriter.WriteValue(poi.Notes);//Encoded to HTML Entities
-
-                                       jsonWriter.WritePropertyName("time");
-                                       jsonWriter.WriteValue(poi.Timestamp);
-                                       
-                                       jsonWriter.WritePropertyName("chunkPos");
-                                       jsonWriter.WriteValue($"{(poi.Location.X / chunkSize)}_{(poi.Location.Z / chunkSize)}");
-                                       
-                                       jsonWriter.WriteEndObject( );
-                                       jsonWriter.WriteEndArray( );
-                               }
-
-                               foreach (var poi in EOIs)
-                               {
-                                       jsonWriter.WriteStartArray( );
-                                       jsonWriter.WriteValue($"{poi.Location.X}_{poi.Location.Z}");
-
-                                       jsonWriter.WriteStartObject( );
-                                       jsonWriter.WritePropertyName("prettyCoord");
-                                       jsonWriter.WriteValue(poi.Location.PrettyCoords(ClientAPI));
-
-                                       jsonWriter.WritePropertyName("notes");
-                                       jsonWriter.WriteValue(poi.Notes);//Encoded to HTML Entities
-
-                                       jsonWriter.WritePropertyName("time");
-                                       jsonWriter.WriteValue(poi.Timestamp);
-
-                                       jsonWriter.WritePropertyName("chunkPos");
-                                       jsonWriter.WriteValue($"{(poi.Location.X / chunkSize)}_{(poi.Location.Z / chunkSize)}");
+               //POI and EOI raw dump files ~ WRITE em!
+               //var poiRawFile = File.
+               string poiPath = Path.Combine(path, poiFileName);
+               string eoiPath = Path.Combine(path, eoiFileName);
+
+               if (this.POIs.Count > 0) {
+               using (var poiFile = File.OpenWrite(poiPath)) {
+                       Serializer.Serialize<PointsOfInterest>(poiFile, this.POIs);
+               }
+               }
 
-                                       jsonWriter.WriteEndObject( );
-                                       jsonWriter.WriteEndArray( );
-                               }
+               if (this.EOIs.Count > 0) {
+               using (var eoiFile = File.OpenWrite(eoiPath)) {
+                       Serializer.Serialize<EntitiesOfInterest>(eoiFile, this.EOIs);
+               }
+               }
 
-                               jsonWriter.WriteEndArray( );
-                               jsonWriter.WriteEndConstructor( );
-                               jsonWriter.WriteRaw(";\n");
-
-                               jsonWriter.WriteWhitespace("\n");
-                               jsonWriter.WriteComment("============= BlockID's for Rockmap / Rock-ratios ===============");
-                               jsonWriter.WriteWhitespace("\n");
-
-                               jsonWriter.WriteRaw("ViewFrame.chunks.rock_Lookup =");
-                               jsonWriter.WriteStartConstructor("Map");
-                               jsonWriter.WriteStartArray( );//An array of... 2-component arrays
-
-                               foreach (var entry in RockIdCodes) {
-                               var block = ClientAPI.World.GetBlock(entry.Key);
-                               
-                               jsonWriter.WriteStartArray( );
-                               jsonWriter.WriteValue(block.Code.Path);
-                                                                       
-                               jsonWriter.WriteStartObject( );
-                               jsonWriter.WritePropertyName("assetCode");
-                               jsonWriter.WriteValue(entry.Value);
-
-                               jsonWriter.WritePropertyName("name");
-                               jsonWriter.WriteValue(Lang.GetUnformatted(block.Code.Path));
-                               //Color?
-
-                               jsonWriter.WriteEndObject( );
-                               jsonWriter.WriteEndArray( );
-                               }
-                               jsonWriter.WriteEndArray( );
-                               jsonWriter.WriteEndConstructor();
-                               
-                               jsonWriter.WriteRaw(";\n");
+               //Create Easy to Parse TSV file for tool/human use....
+               string pointsTsvPath = Path.Combine(path, pointsTsvFileName);
 
-                               jsonWriter.Flush();
+               using (var tsvWriter = new StreamWriter(pointsTsvPath, false, Encoding.UTF8))
+               {
+               tsvWriter.WriteLine("Name\tDescription\tLocation\tTime\t");
+                       foreach (var point in this.POIs) {
+                                       tsvWriter.Write(point.Name + "\t");
+                                       tsvWriter.Write(point.Notes + "\t");
+                                       tsvWriter.Write(point.Location.PrettyCoords(ClientAPI) + "\t");
+                                       tsvWriter.Write(point.Timestamp.ToString("u")+"\t");
+                                       tsvWriter.WriteLine();
                        }
+                       foreach (var entity in this.EOIs) {
+                                       tsvWriter.Write(entity.Name + "\t");
+                                       tsvWriter.Write(entity.Notes + "\t");
+                                       tsvWriter.Write(entity.Location.PrettyCoords(ClientAPI) + "\t");
+                                       tsvWriter.Write(entity.Timestamp.ToString("u") + "\t");
+                                       tsvWriter.WriteLine( );
                        }
-
+               tsvWriter.WriteLine();
+               tsvWriter.Flush( );
                }
 
+               }
 
                private ColumnMeta CreateColumnMetadata(KeyValuePair<Vec2i, uint> mostActiveCol, IMapChunk mapChunk)
                {
-                       ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), ( byte )chunkSize);
+                       ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), (byte) chunkSize);
                        BlockPos equivBP = new BlockPos(mostActiveCol.Key.X * chunkSize,
                                                                                        mapChunk.YMax,
                                                                                        mostActiveCol.Key.Y * chunkSize);
@@ -541,15 +387,15 @@ namespace Automap
                        if (worldmapDir.Exists)
                        {
 
-                               var files = worldmapDir.GetFiles(chunkFile_filter);
+                               var shardFiles = worldmapDir.GetFiles(chunkFile_filter);
 
-                               if (files.Length > 0)
+                               if (shardFiles.Length > 0)
                                {
                                        #if DEBUG
-                                       Logger.VerboseDebug("{0} Existing world chunk shards", files.Length);
+                                       Logger.VerboseDebug("Metadata reloading from {0} shards", shardFiles.Length);
                                        #endif
                                         
-                                       foreach (var shardFile in files) {
+                                       foreach (var shardFile in shardFiles) {
 
                                        if (shardFile.Length < 1024) continue;
                                        var result = chunkShardRegex.Match(shardFile.Name);
@@ -571,23 +417,41 @@ namespace Automap
                                                }
 
                                                }
-                                               catch (PngjException someEx) 
+                                               catch (PngjException someEx)
                                                {
                                                        Logger.Error("PNG Corruption file '{0}' - Reason: {1}", shardFile.Name, someEx);
                                                        continue;
                                                }
+                                               }
+
                                        }
+                               }
+
+                               //POI and EOI raw dump files ~ reload em!
+                               //var poiRawFile = File.
+                               string poiPath = Path.Combine(path, poiFileName);
+                               string eoiPath = Path.Combine(path, eoiFileName);
 
+                               if (File.Exists(poiPath)) {
+                                       using (var poiFile = File.OpenRead(poiPath)) {
+                                       this.POIs = Serializer.Deserialize<PointsOfInterest>(poiFile);
+                                       Logger.VerboseDebug("Reloaded {0} POIs from file.", this.POIs.Count);
                                        }
                                }
 
+                               if (File.Exists(eoiPath)) {
+                                       using (var eoiFile = File.OpenRead(eoiPath)) {
+                                       this.EOIs = Serializer.Deserialize<EntitiesOfInterest>(eoiFile);
+                                       Logger.VerboseDebug("Reloaded {0} EOIs from file.", this.EOIs.Count);
+                                       }
+                               }
 
                        }
                        else
                        {
-#if DEBUG
+                               #if DEBUG
                                Logger.VerboseDebug("Could not open world map directory");
-#endif
+                               #endif
                        }
 
 
@@ -731,9 +595,10 @@ namespace Automap
                {
                        var playerNodePoi = new PointOfInterest()
                        {
+                               Name = "Note",
                                Location = ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Copy(),
                                Notes = notation,
-                               Timestamp = DateTimeOffset.UtcNow,
+                               Timestamp = DateTime.UtcNow,
                        };
 
                        this.POIs.AddReplace(playerNodePoi);
@@ -752,13 +617,12 @@ namespace Automap
                        {
                                switch (cmdData.State)
                                {
-                                       case CommandType.Run:
-                                               CurrentState = cmdData.State;
-                                               AwakenCartographer(0.0f);
-                                               break;
-
+                                       case CommandType.Run:                                                                                           
                                        case CommandType.Stop:
+                                               if (CurrentState != cmdData.State) {
                                                CurrentState = cmdData.State;
+                                               AwakenCartographer(0.0f);
+                                               }
                                                break;
 
                                        case CommandType.Snapshot:
@@ -772,14 +636,9 @@ namespace Automap
                                                AddNote(cmdData.Notation);
                                                break;
                                }
-
                        }
 
-                       if (CurrentState != cmdData.State)
-                       {
-                               CurrentState = cmdData.State;
-                               AwakenCartographer(0.0f);
-                       }
+
 
 #if DEBUG
                        ClientAPI.TriggerChatMessage($"Automap commanded to: {cmdData.State} ");
@@ -788,8 +647,10 @@ namespace Automap
                }
 
 
+
+
                #endregion
 
        }
 
-}
\ No newline at end of file
+}