OSDN Git Service

fully implement the most advanced version of the map so far
authorThe Grand Dog <alex.h@me.com>
Sat, 28 Mar 2020 18:44:50 +0000 (14:44 -0400)
committerThe Grand Dog <alex.h@me.com>
Sat, 28 Mar 2020 18:44:50 +0000 (14:44 -0400)
there are SO many changes

12 files changed:
Automap/Automap.csproj
Automap/Data/ColumnMeta.cs
Automap/Data/DisplayNameAttribute.cs [new file with mode: 0644]
Automap/Data/EntitiesOfInterest.cs
Automap/Data/PointOfInterest.cs
Automap/MapSRC/build.js [new file with mode: 0755]
Automap/MapSRC/src/Automap.css [new file with mode: 0644]
Automap/MapSRC/src/Automap.html [new file with mode: 0644]
Automap/MapSRC/src/ViewFrame.js [new file with mode: 0644]
Automap/MapSRC/src/ViewFrameUtils.js [new file with mode: 0644]
Automap/MapSRC/src/index.js [new file with mode: 0644]
Automap/Subsystems/AutomapSystem.cs

index 76f41bb..24c5382 100644 (file)
@@ -95,6 +95,7 @@
     <Compile Include="Data\CommandData.cs" />
     <Compile Include="Data\CommandType.cs" />
     <Compile Include="Data\PersistedConfiguration.cs" />
+    <Compile Include="Data\DisplayNameAttribute.cs" />
   </ItemGroup>
   <ItemGroup>
     <Folder Include="VS_libs\" />
index 102fa2d..9fd9aef 100644 (file)
@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.Collections.Specialized;
 
 
@@ -8,7 +7,10 @@ using Vintagestory.API.MathTools;
 using Vintagestory.API.Common;
 
 using ProtoBuf;
-
+using System.IO;
+using System.Collections.ObjectModel;
+using System.Text;
+using Vintagestory.API.Client;
 
 namespace Automap
 {
@@ -16,36 +18,47 @@ namespace Automap
        public struct ColumnMeta
        {
                [ProtoMember(1)]
+               [DisplayName(0, "Coords.")]
                public Vec2i Location;
 
                [ProtoMember(2)]
+               [DisplayName(1, "Age")]
                public TimeSpan ChunkAge;//OLDEST CHUNK. from chunk last edit
 
                [ProtoMember(3)]
+               [DisplayName(2, "Temp.")]
                public float Temperature;// Temperature - surface
 
                [ProtoMember(4)]
+               [DisplayName(3, "Y Max.")]
                public ushort YMax;// Y feature height
 
                [ProtoMember(5)]
+               //[DisplayName(10, "Rocks")]
                public Dictionary<int, uint> RockRatio;//[Column] Geographic region (rock) Ratio. [BlockID * count]
 
                [ProtoMember(6)]
+               [DisplayName(4, "Fert.")]
                public float Fertility;
 
                [ProtoMember(7)]
+               [DisplayName(5, "Forest")]
                public float ForestDensity;
 
                [ProtoMember(8)]
+               [DisplayName(6, "Rain")]
                public float Rainfall;
 
                [ProtoMember(9)]
+               [DisplayName(7, "Shrub")]
                public float ShrubDensity;
 
                [ProtoMember(10)]
+               [DisplayName(8, "Air blocks")]
                public uint AirBlocks;
 
                [ProtoMember(11)]
+               [DisplayName(9, "Non-air")]
                public uint NonAirBlocks;
 
                [ProtoMember(12)]
@@ -56,7 +69,7 @@ namespace Automap
                public ushort[,] HeightMap;//Needs to be 'flattened' for Protocol-Buffer serialization
 
                [ProtoMember(13)]
-               private ushort[ ] _flattened_HeightMap;
+               private ushort[] _flattened_HeightMap;
 
 
                public ColumnMeta(Vec2i loc, byte chunkSize = 32)
@@ -89,48 +102,70 @@ namespace Automap
                        this.YMax = mapChunk.YMax;
                }
 
-
+               public void Write(StreamWriter stream, ICoreClientAPI ClientApi)
+               {
+                       // this is gross i hate this
+                       stream.Write("['{0}_{1}',[",
+                               Location.X,
+                               Location.Y
+                               );
+                       stream.Write("'{0}',", Location.PrettyCoords(ClientApi));
+                       stream.Write("'{0}',", ChunkAge);
+                       stream.Write("'{0}',", Temperature.ToString("F3"));
+                       stream.Write("'{0}',", YMax);
+                       stream.Write("'{0}',", Fertility.ToString("F3"));
+                       stream.Write("'{0}',", ForestDensity.ToString("F3"));
+                       stream.Write("'{0}',", Rainfall.ToString("F3"));
+                       stream.Write("'{0}',", ShrubDensity.ToString("F3"));
+                       stream.Write("'{0}',", AirBlocks);
+                       stream.Write("'{0}',", NonAirBlocks);
+                       stream.Write("]]");
+               }
 
                [ProtoBeforeSerialization]
-               private void PrepareData( )
+               private void PrepareData()
                {
 
-               if (HeightMap != null) {
-                       _flattened_HeightMap = new ushort[ChunkSize * ChunkSize];
-               int flatIndex = 0;
+                       if (HeightMap != null)
+                       {
+                               _flattened_HeightMap = new ushort[ChunkSize * ChunkSize];
+                               int flatIndex = 0;
+
+                               for (byte col = 0; col < ChunkSize; col++)
+                               {
+                                       for (byte row = 0; row < ChunkSize; row++)
+                                       {
+                                               _flattened_HeightMap[flatIndex] = HeightMap[col, row];
+                                               flatIndex++;
+                                       }
+                               }
 
-               for (byte col = 0; col < ChunkSize; col++) {
-                       for (byte row = 0; row < ChunkSize; row++) {
-                       _flattened_HeightMap[flatIndex] = HeightMap[col, row];
-                       flatIndex++;
                        }
-               }
-
-               }
 
                }
 
 
                [ProtoAfterDeserialization]
-               private void PostProcess( )
+               private void PostProcess()
                {
-               if (this.HeightMap == null)     this.HeightMap = new ushort[ChunkSize, ChunkSize];
-
-               if (_flattened_HeightMap != null) {
-               int col, row;
+                       if (this.HeightMap == null) this.HeightMap = new ushort[ChunkSize, ChunkSize];
 
-               BitVector32 bitMasker = new BitVector32(0);
-               var rowSection = BitVector32.CreateSection(( short )(ChunkSize - 1));
-               var colSection = BitVector32.CreateSection(( short )(ChunkSize - 1), rowSection);
-
-               for (uint rowcol = 0; rowcol < (ChunkSize * ChunkSize); rowcol++) {
-               bitMasker = new BitVector32(data: ( int )rowcol);
-               row = bitMasker[rowSection];
-               col = bitMasker[colSection];
-               HeightMap[col, row] = _flattened_HeightMap[rowcol];
-               }
+                       if (_flattened_HeightMap != null)
+                       {
+                               int col, row;
+                               _ = new BitVector32(0);
+                               var rowSection = BitVector32.CreateSection((short) (ChunkSize - 1));
+                               var colSection = BitVector32.CreateSection((short) (ChunkSize - 1), rowSection);
+
+                               for (uint rowcol = 0; rowcol < (ChunkSize * ChunkSize); rowcol++)
+                               {
+                                       BitVector32 bitMasker = new BitVector32(data: (int) rowcol);
+                                       row = bitMasker[rowSection];
+                                       col = bitMasker[colSection];
+                                       HeightMap[col, row] = _flattened_HeightMap[rowcol];
+                               }
 
-               }
+                       }
 
                }
 
@@ -217,5 +252,4 @@ namespace Automap
                }
 
        }
-}
-
+}
\ No newline at end of file
diff --git a/Automap/Data/DisplayNameAttribute.cs b/Automap/Data/DisplayNameAttribute.cs
new file mode 100644 (file)
index 0000000..c767fb3
--- /dev/null
@@ -0,0 +1,17 @@
+using System;
+using System.Reflection;
+
+namespace Automap
+{
+       [AttributeUsage(AttributeTargets.Field)]
+       public class DisplayNameAttribute : Attribute
+       {
+               public string name;
+               public byte order;
+               public DisplayNameAttribute(byte order, string name)
+               {
+                       this.order = order;
+                       this.name = name;
+               }
+       }
+}
\ No newline at end of file
index 9760c3b..1da27e9 100644 (file)
@@ -1,11 +1,11 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
-
+using System.IO;
 using System.Linq;
-
 using Vintagestory.API.Common.Entities;
 using Vintagestory.API.MathTools;
+using Vintagestory.API.Client;
 
 namespace Automap
 {
@@ -14,10 +14,27 @@ namespace Automap
        /// </summary>
        public struct EntityOfInterest
        {
+               [DisplayName(1, "Notes")]
                public string Notes;
+               [DisplayName(0, "Loc.")]
                public BlockPos Location;
+               [DisplayName(2, "Time")]
                public DateTimeOffset Timestamp;
+               [DisplayName(3, "ID")]
                public long EntityId;
+               public void Write(StreamWriter stream, ICoreClientAPI ClientApi)
+               {
+                       // this is gross i hate this
+                       stream.Write("['{0}_{1}',[",
+                               Location.X,
+                               Location.Y
+                               );
+                       stream.Write("'{0}',", Location.PrettyCoords(ClientApi));
+                       stream.Write("'{0}',", System.Web.HttpUtility.HtmlEncode(Notes));
+                       stream.Write("'{0}',", Timestamp);
+                       stream.Write("'{0}',", EntityId);
+                       stream.Write("]]");
+               }
        }
 
        /// <summary>
index b19335d..4876957 100644 (file)
@@ -1,6 +1,7 @@
 using System;
 using System.Collections.ObjectModel;
-
+using System.IO;
+using Vintagestory.API.Client;
 using Vintagestory.API.Common;
 using Vintagestory.API.MathTools;
 
@@ -11,9 +12,24 @@ namespace Automap
        /// </summary>
        public struct PointOfInterest
        {
+               [DisplayName(1, "Notes")]
                public string Notes;
+               [DisplayName(0, "Loc.")]
                public BlockPos Location;
+               [DisplayName(2, "Time")]
                public DateTimeOffset Timestamp;
+               public void Write(StreamWriter stream, ICoreClientAPI ClientApi)
+               {
+                       // this is gross i hate this
+                       stream.Write("['{0}_{1}',[",
+                               Location.X,
+                               Location.Y
+                               );
+                       stream.Write("'{0}',", Location.PrettyCoords(ClientApi));
+                       stream.Write("'{0}',", System.Web.HttpUtility.HtmlEncode(Notes).Replace("\n", "&#13;&#10;").Replace("\\","\\\\"));
+                       stream.Write("'{0}',", Timestamp);
+                       stream.Write("]]");
+               }
        }
 
        public class PointsOfInterest : KeyedCollection<BlockPos, PointOfInterest>
diff --git a/Automap/MapSRC/build.js b/Automap/MapSRC/build.js
new file mode 100755 (executable)
index 0000000..7875da0
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/local/bin/node
+
+// im sorry
+// cd into MapSRC and run node ./build.js and this will concat and stuff
+const fs = require('fs');
+
+fs.readFile('./src/Automap.html', 'utf8', (err, d) => {
+       if (err) console.log(err);
+       let outD = d.replace(/<link rel=\"stylesheet\" href=\"(.*)\">/g, '<style>REP:$1</style>')
+               .replace(/(<script type=\"text\/javascript\") src=\"(.*)\">/g, '$1>REP:$2')
+               .replace(/REP:(\w*\.\w*)/g, (match, name, offset, string) => {
+                       return fs.readFileSync('./src/' + name, 'utf8')
+                               .replace(/\/\/.*\n?/g, '');
+               })
+               .replace(/[\t\n]/g, '');
+       fs.writeFile('./dist/automap.html', outD, err => {
+               if (err) console.log(err);
+       });
+       fs.writeFile('../assets/automap/config/automap.html', outD, err => {
+               if (err) console.log(err);
+       });
+});
\ No newline at end of file
diff --git a/Automap/MapSRC/src/Automap.css b/Automap/MapSRC/src/Automap.css
new file mode 100644 (file)
index 0000000..44e30e5
--- /dev/null
@@ -0,0 +1,38 @@
+html, body, .map {
+       width: 100%;
+       height: 100%;
+       margin: 0;
+       overflow: hidden;
+       outline: 1px dotted black;
+}
+
+.infobox {
+       width: 15em;
+       /* height: 15em; */
+       background-color: rgba(200, 200, 200, 0.5);
+       left: 0;
+       top: 0;
+       font-family: sans-serif;
+       position: absolute;
+       z-index: 1;
+}
+
+h1 {
+       font-size: 22px;
+       margin: .6em 1em;
+       text-align: center;
+}
+
+.infoboxTable {
+       margin: .3em auto;
+       width: 90%;
+       outline: 1px solid #333;
+}
+
+.infoboxTable th {
+       width: 30%;
+}
+
+.infoboxTable td {
+       text-align: right;
+}
\ No newline at end of file
diff --git a/Automap/MapSRC/src/Automap.html b/Automap/MapSRC/src/Automap.html
new file mode 100644 (file)
index 0000000..5b9ef8a
--- /dev/null
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+
+<head>
+       <meta charset="utf-8">
+       <title>Automap</title>
+       <link rel="stylesheet" href="Automap.css">
+</head>
+
+<body>
+       <script type="text/javascript" src="ViewFrame.js"></script>
+       <script type="text/javascript" src="ViewFrameUtils.js"></script>
+       <div class="infobox">
+               <h1>Chunk Info</h1>
+               <table class="infoboxTable">
+               </table>
+       </div>
+       <script type="text/javascript" src="index.js"></script>
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/Automap/MapSRC/src/ViewFrame.js b/Automap/MapSRC/src/ViewFrame.js
new file mode 100644 (file)
index 0000000..663a50f
--- /dev/null
@@ -0,0 +1,140 @@
+function ViewFrame() {
+       // dom stuff
+       const map = document.createElement('canvas'); // the map we see
+       map.className = 'map';
+       map.height = window.innerHeight;
+       map.width = window.innerWidth;
+       document.getElementsByTagName('body')[0].append(map);
+       // this is the map that we actually draw on
+       this.map = map.getContext('2d', {
+               alpha: false
+       });
+       this.map.imageSmoothingEnabled = false;
+
+       // the info box in the corner
+       this.infobox = document.getElementsByClassName('infoboxTable')[0];
+       this.infoboxSlots = new Array();
+
+       // load the metadata!
+       this.chunkScript = document.createElement('script');
+       this.chunkScript.type = 'text/javascript';
+       this.chunkScript.src = 'Metadata.js';
+       document.getElementsByTagName('body')[0].append(this.chunkScript);
+       this.chunkScript.addEventListener('load', () => {
+               ViewFrame.initInfobox(this.infobox, this.infoboxSlots);
+               this.x = ViewFrame.chunks.startCoords[0];
+               this.y = ViewFrame.chunks.startCoords[1];
+               this.availableChunks = ViewFrame.chunks.chunkMetadata;
+               this.render();
+       }, {
+               once: true
+       });
+
+       // Tracks images that have been loaded and are on the map
+       this.loadedChunksByName = new Map();
+       // this is needed because [1, 2] != [1, 2] and thats how we store coords.
+       this.loadedChunksByCoords = new Map();
+       this.availableChunks = null; // the chunks in ./Metadata.js
+       // so that we dont render twice at the same time
+       this.rendering = false;
+
+       this.x = -1;
+       this.y = -1; // can be fractional
+       this.zoom = 32; // pixels wide the images are to be
+       this.updateEdges();
+}
+// prototypes, some less... notable? methods are
+// in ViewFrameUtils.js 
+ViewFrame.prototype.reloadChunkList = function () {
+       if (this.chunkScript) {
+               this.chunkScript.remove();
+               delete this.chunkScript;
+       }
+
+       this.chunkScript = document.createElement('script');
+       this.chunkScript.type = 'text/javascript';
+       this.chunkScript.src = 'Metadata.js';
+       document.getElementsByTagName('body')[0].append(this.chunkScript);
+
+       this.chunkScript.addEventListener('load', () => {
+               this.availableChunks = ViewFrame.chunks.chunkMetadata;
+               this.render();
+       });
+};
+
+ViewFrame.prototype.render = function () {
+       if (!this.availableChunks) return;
+       if (this.rendering) clearInterval(ViewFrame.intervalRef);
+       this.rendering = true;
+       this.updateEdges();
+       this.map.clearRect(0, 0, window.innerWidth, window.innerHeight);
+       // culling
+       this.loadedChunksByCoords
+               .forEach((chunk, coord) => { // check the bounds
+                       if (coord[0] < this.eastChunk &&
+                               coord[0] >= this.westChunk &&
+                               coord[1] <= this.northChunk &&
+                               coord[1] >= this.southChunk) {
+
+                               this.place(chunk, coord[0], coord[1]);
+                               return;
+                       }
+                       // its out of range!!!
+                       // get 'em boys!!!!!
+                       this.loadedChunksByCoords.delete(coord);
+                       this.loadedChunksByName.delete(coord.join('_'));
+                       chunk.remove();
+               });
+
+       // gathering what we need to load
+       const neededChunks = new Set();
+       for (var x = this.westChunk; x < this.eastChunk; x++) {
+               for (var y = this.southChunk; y < this.northChunk; y++) {
+                       const chunKey = [x, y]; // chunk + key = chunKey :)
+                       const name = chunKey.join('_');
+                       // continue if its not available, or it is loaded
+                       if (!this.availableChunks.has(name) ||
+                               this.loadedChunksByName.has(name)) continue;
+                       neededChunks.add(chunKey);
+               }
+       }
+       // iterating over everything we need to load
+       const it = neededChunks.values();
+       ViewFrame.intervalRef = setInterval(() => {
+               let round = it.next();
+               if (!round.done) {
+                       // load
+                       const img = new Image(32, 32);
+                       const name = round.value.join('_');
+
+                       img.src = name + '.png';
+
+                       decode(img, loadedImage => {
+                               this.place(img, round.value[0], round.value[1]);
+                       });
+                       this.loadedChunksByName.set(name, img);
+                       this.loadedChunksByCoords.set(round.value, img);
+               } else {
+                       clearInterval(ViewFrame.intervalRef);
+                       this.rendering = false;
+               }
+       }, 4);
+};
+
+ViewFrame.prototype.place = function (img, x, y) {
+       x -= this.x;
+       y -= this.y;
+       x *= this.zoom;
+       y *= this.zoom;
+       x += this.width / 2;
+       y += this.height / 2;
+
+       this.map.drawImage(img, Math.floor(x), Math.floor(y), this.zoom, this.zoom);
+};
+
+ViewFrame.prototype.updateInfobox = function (chunkName) {
+       const chunkMeta = this.availableChunks.get(chunkName);
+       this.infoboxSlots.forEach((l, k) => {
+               l.innerText = chunkMeta ? chunkMeta[k] : '0';
+       });
+};
\ No newline at end of file
diff --git a/Automap/MapSRC/src/ViewFrameUtils.js b/Automap/MapSRC/src/ViewFrameUtils.js
new file mode 100644 (file)
index 0000000..dc35335
--- /dev/null
@@ -0,0 +1,62 @@
+ViewFrame.initInfobox = function (ibox, iboxSlots) {
+
+       ViewFrame.chunks
+               .chunkMetadataNames.forEach((item, i) => {
+                       const slot = document.createElement('tr');
+                       const head = document.createElement('th');
+                       head.innerText = item;
+                       const row = document.createElement('td');
+                       row.innerText = '0';
+                       iboxSlots[i] = row;
+                       slot.append(head, row);
+                       ibox.append(slot);
+               });
+};
+
+ViewFrame.prototype.updateEdges = function () {
+       if (this.width != window.innerWidth || this.height != window.innerHeight) {
+               this.width = window.innerWidth;
+               this.map.canvas.width = this.width;
+               this.height = window.innerHeight;
+               this.map.canvas.height = this.height;
+               this.map.imageSmoothingEnabled = false;
+       }
+       const chunksWide = Math.ceil(this.width / this.zoom);
+       const chunksHigh = Math.ceil(this.height / this.zoom);
+
+       this.east = this.x + chunksWide / 2; // this is fractional and is used to keep track of the edges of the window
+       this.eastChunk = Math.ceil(this.east); // this is not and is used to track the chunks that need to load
+       this.west = this.x - chunksWide / 2;
+       this.westChunk = Math.floor(this.west);
+       this.north = this.y + chunksHigh / 2;
+       this.northChunk = Math.ceil(this.north);
+       this.south = this.y - chunksHigh / 2;
+       this.southChunk = Math.floor(this.south);
+};
+
+ViewFrame.prototype.moveCenter = function (dx, dy) {
+       // to pan when we click on the map!
+       this.x += (dx - this.width / 2) / this.zoom;
+       this.y += (dy - this.height / 2) / this.zoom;
+};
+ViewFrame.prototype.setCenter = function (x, y) {
+       this.x = x;
+       this.y = y;
+};
+
+ViewFrame.prototype.clear = function () {
+       this.loadedChunksByName.clear();
+       this.loadedChunksByCoords.clear();
+       if (this.chunkScript) this.chunkScript.remove();
+       delete this.chunkScript;
+       delete ViewFrame.chunks;
+       this.map.clearRect(0, 0, window.innerWidth, window.innerHeight);
+};
+
+function decode(img, cb) {
+       img.decode()
+               .then(() => {
+                       cb(img);
+               })
+               .catch(() => {}); // so images arent added on error
+}
\ No newline at end of file
diff --git a/Automap/MapSRC/src/index.js b/Automap/MapSRC/src/index.js
new file mode 100644 (file)
index 0000000..386dc6a
--- /dev/null
@@ -0,0 +1,93 @@
+const vf = new ViewFrame();
+vf.reloadChunkList();
+
+// the event handlers are in iifes so they dont make unneeded globals.
+// resize, delay re-render to reduce lag.
+(function () {
+       var id;
+       window.addEventListener('resize', () => {
+               clearTimeout(id);
+               id = setTimeout(() => {
+                       vf.render();
+               }, 500);
+       });
+}());
+
+// panning
+(function () {
+       var id;
+       vf.map.canvas.addEventListener('mousedown', event => {
+               clearTimeout(id);
+               vf.moveCenter(event.pageX, event.pageY);
+               id = setTimeout(() => {
+                       vf.render();
+               }, 250);
+       });
+}());
+
+
+// #### CONTROLS ####
+// hovering
+(function () {
+       var lastX = 0;
+       var lastY = 0;
+       vf.map.canvas.addEventListener('mousemove', event => {
+               // only count if the mouse moved more than a chunk
+               let x = Math.floor(vf.x +
+                       (event.clientX - vf.width / 2) / vf.zoom);
+               let y = Math.floor(vf.y +
+                       (event.clientY - vf.height / 2) / vf.zoom);
+               if (x == lastX && y == lastY) return;
+               lastX = x;
+               lastY = y;
+               vf.updateInfobox(x + '_' + y);
+       });
+}());
+
+// scroll/zoom
+(function () {
+       var id;
+       vf.map.canvas.addEventListener('wheel', event => {
+               clearTimeout(id);
+               vf.zoom += -Math.sign(event.deltaY);
+               id = setTimeout(() => {
+                       vf.render();
+               }, 250);
+       });
+}());
+
+// reload the chunk list every six seconds
+(function () {
+       setInterval(() => {
+               vf.reloadChunkList();
+       }, 6000);
+}());
+// 
+// var i = 500;
+// setTimeout(() => {
+//     vf.map.dispatchEvent(new MouseEvent('mousedown', {
+//             clientX: 879,
+//             clientY: 926
+//     }));
+// }, i += 1000);
+// 
+// setTimeout(() => {
+//     vf.map.dispatchEvent(new MouseEvent('mousedown', {
+//             clientX: 666,
+//             clientY: 924
+//     }));
+// }, i += 2000);
+// 
+// setTimeout(() => {
+//     vf.map.dispatchEvent(new MouseEvent('mousedown', {
+//             clientX: 1090,
+//             clientY: 936
+//     }));
+// }, i += 2000);
+// 
+// setTimeout(() => {
+//     vf.map.dispatchEvent(new MouseEvent('mousedown', {
+//             clientX: 29,
+//             clientY: 785
+//     }));
+// }, i += 2000);
\ No newline at end of file
index ba58ae3..53a785f 100644 (file)
@@ -1,8 +1,10 @@
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO;
 using System.Linq;
+using System.Reflection;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Threading;
@@ -38,6 +40,7 @@ namespace Automap
                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; }
@@ -56,7 +59,7 @@ namespace Automap
 
                public static string AutomapStatusEventKey = @"AutomapStatus";
                public static string AutomapCommandEventKey = @"AutomapCommand";
-               PersistedConfiguration cachedConfiguration;
+               //PersistedConfiguration cachedConfiguration;
 
                public AutomapSystem(ICoreClientAPI clientAPI, ILogger logger, PersistedConfiguration config)
                {
@@ -74,7 +77,7 @@ namespace Automap
                        //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.");
@@ -97,6 +100,8 @@ namespace Automap
                        outputText.Write(staticMap.ToText());
                        outputText.Flush();
 
+                       MakePreBuiltJSON();
+
                        Prefill_POI_Designators();
                        startChunkColumn = new Vec2i((ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.X / chunkSize), (ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Z / chunkSize));
                        chunkTopMetadata = new ColumnsMetadata(startChunkColumn);
@@ -132,11 +137,11 @@ namespace Automap
                                Logger.VerboseDebug("Cartographer re-trigger from [{0}]", cartographer_thread.ThreadState);
 #endif
 
-                               if (cartographer_thread.ThreadState.HasFlag(ThreadState.Unstarted))
+                               if (cartographer_thread.ThreadState.HasFlag(System.Threading.ThreadState.Unstarted))
                                {
                                        cartographer_thread.Start();
                                }
-                               else if (cartographer_thread.ThreadState.HasFlag(ThreadState.WaitSleepJoin))
+                               else if (cartographer_thread.ThreadState.HasFlag(System.Threading.ThreadState.WaitSleepJoin))
                                {
                                        //Time to (re)write chunk shards
                                        cartographer_thread.Interrupt();
@@ -168,7 +173,7 @@ namespace Automap
                                if (!columnCounter.IsEmpty)
                                {
                                        var tempSet = columnCounter.ToArray().OrderByDescending(kvp => kvp.Value);
-                                       UpdateEntityMetadata( );
+                                       UpdateEntityMetadata();
 
                                        foreach (var mostActiveCol in tempSet)
                                        {
@@ -200,9 +205,9 @@ namespace Automap
                                                if (updatedPixels > 0)
                                                {
 
-                                                       #if DEBUG
+#if DEBUG
                                                        Logger.VerboseDebug("Wrote chunk shard: ({0}) - Edits#:{1}, Pixels#:{2}", mostActiveCol.Key, mostActiveCol.Value, updatedPixels);
-                                                       #endif
+#endif
                                                        updatedChunks++;
                                                        chunkTopMetadata.Update(chunkMeta);
                                                        columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
@@ -271,7 +276,7 @@ namespace Automap
                }
 
                private void Reload_POI_Designators()
-               {                       
+               {
                        Logger.VerboseDebug("Connecting {0} Configured Block-Designators", configuration.BlockDesignators.Count);
                        foreach (var designator in configuration.BlockDesignators)
                        {
@@ -305,7 +310,6 @@ namespace Automap
 
                }
 
-               //TODO: Rewrite as Newtonsoft  JsonTextWriter !!!
                /// <summary>
                /// Generates the JSON Metadata. (in Map object format )
                /// </summary>
@@ -315,207 +319,84 @@ namespace Automap
 
                        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)
+                       using (stream)
                        {
-                               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");
+                               Stopwatch stopWatch = new Stopwatch();
+                               stopWatch.Start();
 
-                               jsonWriter.WriteRaw("ViewFrame.chunks.chunkSize=");
-                               jsonWriter.WriteValue(chunkSize);
-                               jsonWriter.WriteRaw(";\n");
+                               stream.Write(jsonPreBuilt);
 
-                               jsonWriter.WriteRaw("ViewFrame.chunks.northMostChunk=");
-                               jsonWriter.WriteValue(chunkTopMetadata.North_mostChunk);
-                               jsonWriter.WriteRaw(";\n");
+                               stream.Write("ViewFrame.chunks.genTime='{0}';",
+                                       DateTimeOffset.UtcNow
+                                       );
 
-                               jsonWriter.WriteRaw("ViewFrame.chunks.southMostChunk=");
-                               jsonWriter.WriteValue(chunkTopMetadata.South_mostChunk);
-                               jsonWriter.WriteRaw(";\n");
+                               stream.Write("ViewFrame.chunks.startCoords=[{0},{1}];",
+                                       startChunkColumn.X, startChunkColumn.Y
+                                       );
 
-                               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");
+                               stream.Write("ViewFrame.chunks.bounds=[{0},{1},{2},{3}];",
+                                       chunkTopMetadata.North_mostChunk,
+                                       chunkTopMetadata.South_mostChunk,
+                                       chunkTopMetadata.West_mostChunk,
+                                       chunkTopMetadata.East_mostChunk
+                                       );
 
 
                                //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
-
+                               stream.Write("ViewFrame.chunks.chunkMetadata=new Map([");
 
                                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
+                                       shard.Write(stream, ClientAPI);
+                                       stream.Write(",");
                                }
+                               stream.Write("]);");// Close constructor of Map (chunkMetadata)
 
-                               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
+                               stream.Write("ViewFrame.chunks.pointsOfInterest=new Map([");
 
                                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( );
+                                       poi.Write(stream, ClientAPI);
+                                       stream.Write(",");
                                }
-                               jsonWriter.Write("]);");
 
-                               jsonWriter.Write("ViewFrame.chunks.entitiesOfInterest=new Map([");
+                               stream.Write("]);");
+
+                               stream.Write("ViewFrame.chunks.entitiesOfInterest=new Map([");
                                foreach (var eoi in EOIs)
                                {
-                                       jsonWriter.WriteStartArray( );
-                                       jsonWriter.WriteValue($"{poi.Location.X}_{poi.Location.Z}");
+                                       eoi.Write(stream, ClientAPI);
+                                       stream.Write(",");
+                               }
+                               stream.Write("]);\n");
 
-                                       jsonWriter.WriteStartObject( );
-                                       jsonWriter.WritePropertyName("prettyCoord");
-                                       jsonWriter.WriteValue(poi.Location.PrettyCoords(ClientAPI));
+                               //stream.Write("//============= BlockID's for Rockmap / Rock-ratios ===============");
 
-                                       jsonWriter.WritePropertyName("notes");
-                                       jsonWriter.WriteValue(poi.Notes);//Encoded to HTML Entities
+                               //stream.Write("ViewFrame.chunks.rockLookup=new Map([");
 
-                                       jsonWriter.WritePropertyName("time");
-                                       jsonWriter.WriteValue(poi.Timestamp);
+                               //foreach (var entry in RockIdCodes)
+                               //{
+                               //      var block = ClientAPI.World.GetBlock(entry.Key);
 
-                                       jsonWriter.WritePropertyName("chunkPos");
-                                       jsonWriter.WriteValue($"{(poi.Location.X / chunkSize)}_{(poi.Location.Z / chunkSize)}");
+                               //      stream.Write("[");
+                               //      stream.Write("'{0},", block.Code.Path);
 
-                                       jsonWriter.WriteEndObject( );
-                                       jsonWriter.WriteEndArray( );
-                               }
+                               //      stream.Write("[");
+                               //      stream.Write("'{0}',", entry.Value);
 
-                               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");
+                               //      stream.Write("{0},", Lang.GetUnformatted(block.Code.Path));
+                               //      //Color?
 
-                               jsonWriter.Flush();
-                       }
+                               //      stream.Write("]],");
+                               //}
+
+                               //stream.Write("]);\n");
+
+                               stream.Flush();
+
+                               stopWatch.Stop();
+                               Logger.Debug("Run time of chunk write {0}", stopWatch.Elapsed);
                        }
 
                }
@@ -695,13 +576,6 @@ namespace Automap
                                                        }
 
                                                        chunkMeta.NonAirBlocks++;
-
-                                                       ////Heightmap
-                                                       //if (chunkMeta.HeightMap[X_index, Z_index] == 0)
-                                                       //{
-                                                       //      chunkMeta.HeightMap[X_index, Z_index]
-                                                       //              = (ushort) (Y_index + (targetChunkY * chunkSize));
-                                                       //}
                                                }
                                                while (X_index++ < (chunkSize - 1));
                                                X_index = 0;
@@ -721,9 +595,9 @@ namespace Automap
                        foreach (var loadedEntity in ClientAPI.World.LoadedEntities.ToArray())
                        {
 
-                               #if DEBUG
+#if DEBUG
                                //Logger.VerboseDebug($"ENTITY: ({loadedEntity.Value.Code}) = #{loadedEntity.Value.EntityId} {loadedEntity.Value.State} {loadedEntity.Value.LocalPos}    <<<<<<<<<<<<");
-                               #endif
+#endif
 
                                var dMatch = Entity_Designators.SingleOrDefault(se => se.Key.Equals(loadedEntity.Value.Code));
                                if (dMatch.Value != null)
@@ -798,6 +672,65 @@ namespace Automap
                }
 
 
+               private void MakePreBuiltJSON()
+               {
+                       var builder = new StringBuilder(512);
+                       builder.Append("ViewFrame.chunks={};\n");
+                       builder.AppendFormat("ViewFrame.chunks.worldSeedNum='{0}';",
+                                       ClientAPI.World.Seed
+                                       );
+                       builder.AppendFormat("ViewFrame.chunks.chunkSize={0};",
+                                       chunkSize
+                                       );
+
+                       builder.Append("ViewFrame.chunks.chunkMetadataNames=[");
+                       var fields = typeof(ColumnMeta).GetFields();
+                       var attsToSort = new List<DisplayNameAttribute>();
+                       // this is so gross
+                       foreach (var f in fields)
+                       {
+                               var att = f.GetCustomAttribute(typeof(DisplayNameAttribute));
+                               if (att != null)
+                               {
+                                       attsToSort.Add((DisplayNameAttribute) att);
+                               }
+
+                       }
+                       foreach (var att in attsToSort.OrderBy(a => a.order))
+                               builder.AppendFormat("'{0}',", att.name);
+                       builder.Append("];\n");
+
+                       builder.Append("ViewFrame.chunks.pointsOfInterestNames=[");
+                       fields = typeof(PointOfInterest).GetFields();
+                       attsToSort = new List<DisplayNameAttribute>();
+
+                       foreach (var f in fields)
+                       {
+                               var att = f.GetCustomAttribute(typeof(DisplayNameAttribute));
+                               if (att != null)
+                                       attsToSort.Add((DisplayNameAttribute) att);
+                       }
+                       foreach (var att in attsToSort.OrderBy(a => a.order))
+                               builder.AppendFormat("'{0}',", att.name);
+                       builder.Append("];\n");
+
+                       builder.Append("ViewFrame.chunks.entityOfInterestNames=[");
+                       fields = typeof(EntityOfInterest).GetFields();
+                       attsToSort = new List<DisplayNameAttribute>();
+
+                       foreach (var f in fields)
+                       {
+                               var att = f.GetCustomAttribute(typeof(DisplayNameAttribute));
+                               if (att != null)
+                                       attsToSort.Add((DisplayNameAttribute) att);
+                       }
+                       foreach (var att in attsToSort.OrderBy(a => a.order))
+                               builder.AppendFormat("'{0}',", att.name);
+                       builder.Append("];\n");
+
+                       jsonPreBuilt = builder.ToString();
+               }
+
                #endregion
 
        }