OSDN Git Service

Fixes for Automatic startup
[automap/automap.git] / Automap / Subsystems / AutomapSystem.cs
index 4e85a7e..2e35db3 100644 (file)
@@ -9,11 +9,12 @@ using System.Text.RegularExpressions;
 using System.Threading;
 
 using Hjg.Pngcs;
-
+using Mono.Collections.Generic;
 using ProtoBuf;
 
 using Vintagestory.API.Client;
 using Vintagestory.API.Common;
+using Vintagestory.API.Common.Entities;
 using Vintagestory.API.Config;
 using Vintagestory.API.Datastructures;
 using Vintagestory.API.MathTools;
@@ -24,7 +25,7 @@ namespace Automap
        public class AutomapSystem
        {
                private Thread cartographer_thread;
-               private Thread snapshotThread;
+
                private Snapshotter snapshot;
                private ICoreClientAPI ClientAPI { get; set; }
                private ILogger Logger { get; set; }
@@ -33,7 +34,7 @@ namespace Automap
 
                internal const string _mapPath = @"Maps";
                internal const string _chunkPath = @"Chunks";
-               internal const uint editThreshold = 1;
+               internal const uint editThreshold = 9;
                private const string _domain = @"automap";
                private const string chunkFile_filter = @"*_*.png";
                private const string poiFileName = @"poi_binary";
@@ -43,9 +44,10 @@ namespace Automap
                private static Regex chunkShardRegex = new Regex(@"(?<X>[\d]+)_(?<Z>[\d]+)\.png", RegexOptions.Singleline);
 
                private ConcurrentDictionary<Vec2i, ColumnCounter> columnCounters = new ConcurrentDictionary<Vec2i, ColumnCounter>(3, 150);
+               private Queue<Vec2i> revisitChunkList = new Queue<Vec2i>();
                private ColumnsMetadata chunkTopMetadata;
-               private PointsOfInterest POIs = new PointsOfInterest();
-               private EntitiesOfInterest EOIs = new EntitiesOfInterest();
+               internal PointsOfInterest POIs = new PointsOfInterest();
+               internal EntitiesOfInterest EOIs = new EntitiesOfInterest();
 
                internal Dictionary<int, BlockDesignator> BlockID_Designators { get; private set; }
                internal Dictionary<AssetLocation, EntityDesignator> Entity_Designators { get; private set; }
@@ -79,14 +81,6 @@ namespace Automap
 
                        //Listen on bus for commands
                        ClientAPI.Event.RegisterEventBusListener(CommandListener, 1.0, AutomapSystem.AutomapCommandEventKey);
-
-
-                       if (configuration.Autostart)
-                       {
-                               CurrentState = CommandType.Run;
-                               Logger.Debug("Autostart is Enabled.");
-                       }
-
                }
 
 
@@ -121,15 +115,11 @@ namespace Automap
                                IsBackground = true
                        };
 
-                       snapshot = new Snapshotter(path, chunkTopMetadata, chunkSize,ClientAPI.World.Seed );
-                       snapshotThread = new Thread(Snap)
-                       {
-                               Name = "Snapshot",
-                               Priority = ThreadPriority.Lowest,
-                               IsBackground = true
-                       };
+                       ClientAPI.Event.RegisterGameTickListener(ThreadDecider, 6000);
 
-                       ClientAPI.Event.RegisterGameTickListener(AwakenCartographer, 6000);
+                       if (configuration.Autostart) {
+                               CurrentState = CommandType.Run;
+                       }
                }
 
                private void ChunkAChanging(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason)
@@ -140,18 +130,21 @@ namespace Automap
                columnCounters.AddOrUpdate(topPosition, 
                                              new ColumnCounter(chunkSize, newOrEdit, chunkCoord), 
                                              (chkPos, chkChng) => chkChng.Update(chunkCoord, chunkSize, newOrEdit)
-                                            );
-               
+                                            );         
                }
 
-               private void AwakenCartographer(float delayed)
+               /// <summary>
+               /// Cartographer Thread 'decider'
+               /// </summary>
+               /// <param name="delayed">called delay offset</param>
+               private void ThreadDecider(float delayed)
                {
 
                        if (CurrentState == CommandType.Run && (ClientAPI.IsGamePaused != false || ClientAPI.IsShuttingDown != true))
                        {
-#if DEBUG
-                               Logger.VerboseDebug("Cartographer re-trigger from [{0}]", cartographer_thread.ThreadState);
-#endif
+                               #if DEBUG
+                               Logger.VerboseDebug("ThreadDecider re-trigger from [{0}]", cartographer_thread.ThreadState);
+                               #endif
 
                                if (cartographer_thread.ThreadState.HasFlag(ThreadState.Unstarted))
                                {
@@ -168,15 +161,22 @@ namespace Automap
                        }
                        else if (CurrentState == CommandType.Snapshot)
                        {
-                               if (snapshotThread.ThreadState.HasFlag(ThreadState.Unstarted))
-                               {
-                                       snapshotThread.Start();
-                               } else if (snapshotThread.ThreadState.HasFlag(ThreadState.WaitSleepJoin))
-                               {
-                                       snapshotThread.Interrupt();
+                       //Prepare for taking a snopshot
+                       if (snapshot == null) {         
+                               snapshot = new Snapshotter(path, chunkTopMetadata, chunkSize, ClientAPI.World.Seed);
+                               #if DEBUG
+                               Logger.VerboseDebug("Starting new Snapshot: {0} Wx{1} Hx{2}", snapshot.fileName, snapshot.Width, snapshot.Height);
+                               #endif
+                               snapshot.Take( );
+                               }
+                       else if (snapshot != null && snapshot.Finished) {
+                               #if DEBUG
+                               Logger.VerboseDebug("COMPLETED Snapshot: {0} Wx{1} Hx{2}, taking {3}", snapshot.fileName, snapshot.Width, snapshot.Height, snapshot.Timer.Elapsed);
+                               #endif
+                               snapshot = null;
+                               CurrentState = CommandType.Run;
                                }
                        }
-
                }
 
 
@@ -191,22 +191,64 @@ namespace Automap
                                uint updatedChunks = 0;
                                uint updatedPixels = 0;
 
-                               //-- Should dodge enumerator changing underfoot....at a cost.
-                               if (!columnCounters.IsEmpty)
-                               {
-                                       var tempSet = columnCounters.ToArray().Where(cks => cks.Value.WeightedSum > editThreshold) .OrderByDescending(kvp => kvp.Value.WeightedSum);
+
+               //Revisit failed chunks first;
+               while (revisitChunkList.Count > 0) 
+               {
+               var revisitCoord = revisitChunkList.Dequeue( );
+               var rv_mapChunk = ClientAPI.World.BlockAccessor.GetMapChunk(revisitCoord);
+
+               if (rv_mapChunk == null) continue;
+
+               ColumnMeta rv_chunkMeta;
+               if (chunkTopMetadata.Contains(revisitCoord)) 
+               {
+               rv_chunkMeta = chunkTopMetadata[revisitCoord];
+               #if DEBUG
+               Logger.VerboseDebug("(re)Loaded meta-chunk {0}", revisitCoord);
+               #endif
+               }
+               else                                    
+               {
+               rv_chunkMeta = CreateColumnMetadata(revisitCoord, rv_mapChunk);
+               #if DEBUG
+               Logger.VerboseDebug("(re)Created meta-chunk {0}", revisitCoord);
+               #endif
+               }
+               //TODO: Double validation of....?
+               ProcessChunkBlocks(revisitCoord, rv_mapChunk, ref rv_chunkMeta);
+
+               ChunkRenderer.SetupPngImage(revisitCoord, path, _chunkPath, ref rv_chunkMeta);
+               ChunkRenderer.GenerateChunkPngShard(revisitCoord, rv_mapChunk, rv_chunkMeta, ref chunkTopMetadata, out updatedPixels);
+
+               if (updatedPixels > 0) 
+                       {
+                       #if DEBUG
+                       Logger.VerboseDebug("(re)Wrote top-chunk shard: ({0}), Pixels#:{2}", revisitCoord, updatedPixels);
+                       #endif
+                       updatedChunks++;
+                       chunkTopMetadata.Update(rv_chunkMeta);
+                       }
+               }//*********** REVISIT'd ******************
+               
+               if (!columnCounters.IsEmpty)
+                               {//Resulting snapshot keys mabey NON-UNIQUE!!! a *FEATURE* of ConcurrentDict !
+                                       var columnsSnapshot = columnCounters.Where(cks => cks.Value.WeightedSum > editThreshold).OrderByDescending(kvp => kvp.Value.WeightedSum).ToArray( );
+
                                        UpdateEntityMetadata();
 
-                                       foreach (var mostActiveCol in tempSet)
+                                       foreach (var mostActiveCol in columnsSnapshot)
                                        {
                                                var mapChunk = ClientAPI.World.BlockAccessor.GetMapChunk(mostActiveCol.Key);
 
                                                if (mapChunk == null)
-                                               {
-                                                       //TODO: REVISIT THIS CHUNK!
+                                               {                                                       
+                                                       #if DEBUG
                                                        Logger.Warning("SKIP CHUNK: ({0}) - Map Chunk NULL!", mostActiveCol.Key);
+                                                       #endif
                                                        nullMapCount++;
                                                        columnCounters.TryRemove(mostActiveCol.Key, out ejectedItem);
+                                                       revisitChunkList.Enqueue(mostActiveCol.Key);
                                                        continue;
                                                }
 
@@ -225,10 +267,20 @@ namespace Automap
                                                        Logger.VerboseDebug("Created meta-chunk {0}", mostActiveCol.Key);
                                                        #endif
                                                }
+
+                                               /********* Special Interlock with C.D. *************/
+                                               if (columnCounters.TryRemove(mostActiveCol.Key, out ejectedItem)) {
                                                ProcessChunkBlocks(mostActiveCol.Key, mapChunk, ref chunkMeta);
+                                               ejectedItem.SetCutoff(chunkMeta.YMax / chunkSize);
 
                                                ChunkRenderer.SetupPngImage(mostActiveCol.Key, path, _chunkPath, ref chunkMeta);
                                                ChunkRenderer.GenerateChunkPngShard(mostActiveCol.Key, mapChunk, chunkMeta, ref chunkTopMetadata, out updatedPixels);
+                                               }
+                                               else {
+                                               #if DEBUG
+                                               Logger.Warning("Prevented duplicate processing of: {0}",mostActiveCol.Key );
+                                               #endif
+                                               }
 
                                                if (updatedPixels > 0)
                                                {
@@ -237,14 +289,15 @@ namespace Automap
                                                        #endif
                                                        updatedChunks++;
                                                        chunkTopMetadata.Update(chunkMeta);
-                                                       columnCounters.TryRemove(mostActiveCol.Key, out ejectedItem);
+                                                       //columnCounters.TryRemove(mostActiveCol.Key, out ejectedItem);
                                                }
                                                else
                                                {
-                                                       columnCounters.TryRemove(mostActiveCol.Key, out ejectedItem);
+                                                       //columnCounters.TryRemove(mostActiveCol.Key, out ejectedItem);
                                                        #if DEBUG
                                                        Logger.VerboseDebug("Un-painted chunk shard: ({0}) ", mostActiveCol.Key);
                                                        #endif
+                                                       revisitChunkList.Enqueue(mostActiveCol.Key);
                                                }
                                        }
                                }
@@ -269,32 +322,32 @@ namespace Automap
                                columnCounters.Clear( );
 
                                //Then sleep until interupted again, and repeat
-#if DEBUG
+                               #if DEBUG
                                Logger.VerboseDebug("Thread '{0}' about to sleep indefinitely.", Thread.CurrentThread.Name);
-#endif
+                               #endif
                                Thread.Sleep(Timeout.Infinite);
 
                        }
                        catch (ThreadInterruptedException)
                        {
 
-#if DEBUG
+                       #if DEBUG
                                Logger.VerboseDebug("Thread '{0}' interupted [awoken]", Thread.CurrentThread.Name);
-#endif
+                       #endif
                                goto wake;
 
                        }
                        catch (ThreadAbortException)
                        {
-#if DEBUG
+                       #if DEBUG
                                Logger.VerboseDebug("Thread '{0}' aborted.", Thread.CurrentThread.Name);
-#endif
+                       #endif
                        }
                        finally
                        {
-#if DEBUG
+                       #if DEBUG
                                Logger.VerboseDebug("Thread '{0}' executing finally block.", Thread.CurrentThread.Name);
-#endif
+                       #endif
                                PersistPointsData();
                                Write_PlainMetadata( );
                        }
@@ -303,23 +356,23 @@ namespace Automap
                private void Snap()
                {
                        snapshotTake:
-#if DEBUG
+                       #if DEBUG
                        Logger.VerboseDebug("Snapshot started");
-#endif
+                       #endif
                        try
                        {
                                snapshot.Take();
-#if DEBUG
+                               #if DEBUG
                                Logger.VerboseDebug("Snapshot sleeping");
-#endif
+                               #endif
                                CurrentState = CommandType.Run;
                                Thread.Sleep(Timeout.Infinite);
                        }
                        catch (ThreadInterruptedException)
                        {
-#if DEBUG
+                               #if DEBUG
                                Logger.VerboseDebug("Snapshot intertupted");
-#endif
+                               #endif
                                goto snapshotTake;
                        }
                }
@@ -340,46 +393,58 @@ namespace Automap
 
                        var airBlocksQuery = from airyBlock in ClientAPI.World.Blocks
                                                         where airyBlock.MatterState == EnumMatterState.Solid
-                                                        where airyBlock.BlockMaterial == EnumBlockMaterial.Plant || airyBlock.BlockMaterial == EnumBlockMaterial.Leaves
-                                                        where airyBlock.CollisionBoxes == null || airyBlock.CollisionBoxes.Length == 0
-                                                        select airyBlock;                      
-                       //^^ 'Solid' phase - 'Plant' Blocks without any boundg box ? Except water...
-                       this.AiryIdCodes = airBlocksQuery.ToDictionary(aBlk => aBlk.BlockId, aBlk => aBlk.Code.Path);
-
-                       //Add special marker types for BlockID's of "Interest", overwrite colour, and method
-                       Reload_POI_Designators();
+                                                        where airyBlock.BlockMaterial == EnumBlockMaterial.Plant || airyBlock.BlockMaterial == EnumBlockMaterial.Leaves 
+                                                        where airyBlock.CollisionBoxes == null || airyBlock.CollisionBoxes.Length == 0 || airyBlock.RainPermeable == true                                       
+                                                               select airyBlock;                       
+                       //^^ 'Solid' phase - 'Plant' Blocks without any bounding-box; OR 'Invisible' shapes...
+                       var invisibleBlocksQuery = from novisBlock in ClientAPI.World.Blocks                                                                       
+                                                                          where novisBlock.Shape == null || novisBlock.Shape.Base.EndsWith(GlobalConstants.DefaultDomain, @"invisible")   //Whaat! [ base: "block/basic/invisible" ]
+                                                                               select novisBlock;                      
+                       this.AiryIdCodes = airBlocksQuery.Union(invisibleBlocksQuery).ToDictionary(aBlk => aBlk.BlockId, aBlk => aBlk.Code.Path);
+
+                       #if DEBUG
+                       foreach (var fluffBlock in AiryIdCodes) {
+                       Logger.VerboseDebug("ID#\t{0}:\t{1} IGNORED", fluffBlock.Key, fluffBlock.Value);
+                       }
+                       Logger.VerboseDebug("Ignoring {0} blocks", AiryIdCodes.Count);
+                       #endif
+
+               //Add special marker types for BlockID's of "Interest", overwrite colour, and method
+               Reload_POI_Designators();
                }
 
                private void Reload_POI_Designators()
                {
-                       Logger.VerboseDebug("Connecting {0} Configured Block-Designators", configuration.BlockDesignators.Count);
+               uint poisSetup =0, eoiSetup = 0;
                        foreach (var designator in configuration.BlockDesignators)
                        {
+                               if (designator.Enabled == false) continue;
                                var blockIDs = Helpers.ArbitrarytBlockIdHunter(ClientAPI, designator.Pattern, designator.Material);
                                if (blockIDs.Count > 0) { Logger.VerboseDebug("Designator {0} has {1} associated blockIDs", designator.ToString(), blockIDs.Count); }
                                foreach (var entry in blockIDs)
                                {
                                        BlockID_Designators.Add(entry.Key, designator);
+                                       poisSetup++;
                                }
                        }
                        this.ChunkRenderer.BlockID_Designators = BlockID_Designators;
+                       Logger.VerboseDebug("Connected {0} IDs from {1} Block-Designators", poisSetup, configuration.BlockDesignators.Count );
 
 
-                       Logger.VerboseDebug("Connecting {0} Configured Entity-Designators", configuration.EntityDesignators.Count);
                        foreach (var designator in configuration.EntityDesignators)
                        {
+                               if (designator.Enabled == false) continue;
                                //Get Variants first, from EntityTypes...better be populated!
                                var matched = ClientAPI.World.EntityTypes.FindAll(entp => entp.Code.BeginsWith(designator.Pattern.Domain, designator.Pattern.Path));
 
                                foreach (var match in matched)
-                               {
+                               {                                       
                                        Logger.VerboseDebug("Linked Entity: {0} Designator: {1}", match.Code, designator);
                                        this.Entity_Designators.Add(match.Code, designator);
+                                       eoiSetup++;
                                }
-
-                               //EntityProperties props = ClientAPI.World.GetEntityType(designator.Pattern);
                        }
-
+                       Logger.VerboseDebug("Connected {0} IDs from {1} Entity-Designators", eoiSetup, configuration.EntityDesignators.Count);
 
                }
 
@@ -397,17 +462,19 @@ namespace Automap
 
                        if (this.POIs.Count > 0)
                        {
-                               using (var poiFile = File.OpenWrite(poiPath))
+                               using (var poiFile = File.Open(poiPath, FileMode.Create, FileAccess.Write, FileShare.None))
                                {
                                        Serializer.Serialize<PointsOfInterest>(poiFile, this.POIs);
+                                       poiFile.Flush(true);
                                }
                        }
 
                        if (this.EOIs.Count > 0)
                        {
-                               using (var eoiFile = File.OpenWrite(eoiPath))
+                               using (var eoiFile = File.Open(eoiPath, FileMode.Create, FileAccess.Write, FileShare.None))
                                {
                                        Serializer.Serialize<EntitiesOfInterest>(eoiFile, this.EOIs);
+                                       eoiFile.Flush(true);
                                }
                        }
 
@@ -416,31 +483,33 @@ namespace Automap
 
                        using (var tsvWriter = new StreamWriter(pointsTsvPath, false, Encoding.UTF8))
                        {
-                               tsvWriter.WriteLine("Name\tDescription\tLocation\tTime\tDestination");
+                               tsvWriter.WriteLine("Name\tDescription\tLocation\tTime\tDestination\tEntity_UID");
                                foreach (var point in this.POIs)
                                {
                                        tsvWriter.Write(point.Name + "\t");
                                        var notes = point.Notes
-                                               .Replace("\n", "\\n")
+                                               .Replace('\n', '\x001f')
                                                .Replace("\t", "\\t")
                                                .Replace("\\", "\\\\");
                                        tsvWriter.Write(notes + "\t");
                                        tsvWriter.Write(point.Location.PrettyCoords(ClientAPI) + "\t");
                                        tsvWriter.Write(point.Timestamp.ToString("u") + "\t");
                                        tsvWriter.Write((point.Destination != null ? point.Destination.PrettyCoords(ClientAPI) : "---") +"\t");
+                                       tsvWriter.Write("null\t");
                                        tsvWriter.WriteLine();
                                }
                                foreach (var entity in this.EOIs)
                                {
                                        tsvWriter.Write(entity.Name + "\t");
                                        var notes = entity.Notes
-                                               .Replace("\n", "\\n")
+                                               .Replace('\n', '\x001f')
                                                .Replace("\t", "\\t")
                                                .Replace("\\", "\\\\");
                                        tsvWriter.Write(notes + "\t");
                                        tsvWriter.Write(entity.Location.PrettyCoords(ClientAPI) + "\t");
                                        tsvWriter.Write(entity.Timestamp.ToString("u") + "\t");
-                                       tsvWriter.Write("n/a\t");
+                                       tsvWriter.Write("---\t");
+                                       tsvWriter.Write(entity.EntityId.ToString("D"));
                                        tsvWriter.WriteLine();
                                }
                                tsvWriter.WriteLine();
@@ -457,15 +526,19 @@ namespace Automap
                using (var mdWriter = new StreamWriter(metaDataFile, Encoding.ASCII)) 
                        {
                                mdWriter.WriteLine("WorldSeed {0}", ClientAPI.World.Seed);
-                               mdWriter.WriteLine("PlayerCoords {0:D} {1:D}", startChunkColumn.X, startChunkColumn.Y);
+                               mdWriter.WriteLine("PlayerChunkCoords {0:D} {1:D}", startChunkColumn.X, startChunkColumn.Y);
                                mdWriter.WriteLine("DefaultSpawnPos {0:D} {1:D} {2:D}", ClientAPI.World.DefaultSpawnPosition.AsBlockPos.X,ClientAPI.World.DefaultSpawnPosition.AsBlockPos.Y,ClientAPI.World.DefaultSpawnPosition.AsBlockPos.Z);
+                               //mdWriter.WriteLine("CurrentPlayerSpawn", ClientAPI.World.Player.WorldData.EntityPlayer.);
                                mdWriter.WriteLine("ChunkSize {0}", chunkSize);
                                mdWriter.WriteLine("SeaLevel {0:D}", ClientAPI.World.SeaLevel);
                                mdWriter.WriteLine("WorldSize {0:D} {1:D} {2:D}", ClientAPI.World.BulkBlockAccessor.MapSizeX, ClientAPI.World.BulkBlockAccessor.MapSizeY,ClientAPI.World.BulkBlockAccessor.MapSizeZ);
                                mdWriter.WriteLine("RegionSize {0:D}", ClientAPI.World.BulkBlockAccessor.RegionSize);
-                               mdWriter.WriteLine("Version '{0}'", ClientAPI.Self().Info.Version);
+                               mdWriter.WriteLine("AMVersion '{0}'", ClientAPI.Self().Info.Version);
                                mdWriter.WriteLine("PlayTime {0:F1}", ClientAPI.InWorldEllapsedMilliseconds / 1000);
                                mdWriter.WriteLine("GameDate {0}", ClientAPI.World.Calendar.PrettyDate());
+                               mdWriter.WriteLine("Chunks {0:D}", chunkTopMetadata.Count);
+                               mdWriter.WriteLine("Chunks Updated {0:D}", updatedChunksTotal);
+                               mdWriter.WriteLine("Null Chunks {0:D}", nullChunkCount);        
                                mdWriter.Flush( );
                        }
                }
@@ -485,6 +558,19 @@ namespace Automap
                        return data;
                }
 
+        private ColumnMeta CreateColumnMetadata(Vec2i coords, IMapChunk mapChunk)
+               {
+               ColumnMeta data = new ColumnMeta(coords, ClientAPI, ( byte )chunkSize, (ClientAPI.World.BlockAccessor.MapSizeY / chunkSize));
+               BlockPos equivBP = new BlockPos(coords.X * chunkSize,
+                                                                               mapChunk.YMax,
+                                                                               coords.Y * chunkSize);
+
+               var climate = ClientAPI.World.BlockAccessor.GetClimateAt(equivBP);
+               data.UpdateFieldsFrom(climate, mapChunk, TimeSpan.FromHours(ClientAPI.World.Calendar.TotalHours));
+
+               return data;
+               }
+
                /// <summary>
                /// Reload chunk bounds from chunk shards
                /// </summary>
@@ -596,10 +682,10 @@ namespace Automap
                        {
                                WorldChunk worldChunk = ClientAPI.World.BlockAccessor.GetChunk(key.X, targetChunkY, key.Y) as WorldChunk;
 
-                               if (worldChunk == null || worldChunk.BlockEntities == null)
+                               if (worldChunk == null || worldChunk.BlockEntities == null || worldChunk.Disposed)
                                {
                                        #if DEBUG
-                                       Logger.VerboseDebug("WORLD chunk: null or empty X{0} Y{1} Z{2} !", key.X, targetChunkY, key.Y);
+                                       Logger.VerboseDebug("WORLD chunk, null/disposed OR B.E. null: X{0} Y{1} Z{2} !", key.X, targetChunkY, key.Y);
                                        #endif
                                        nullChunkCount++;
                                        continue;
@@ -607,23 +693,34 @@ namespace Automap
 
                                if (worldChunk.IsPacked()) 
                                {
+                               #if DEBUG
                                Logger.VerboseDebug("WORLD chunk: Compressed: X{0} Y{1} Z{2}", key.X, targetChunkY, key.Y);
-                               worldChunk.Unpack( );//RESEARCH: Thread Unsafe? 
+                               #endif
+                               //VALIDATE: check Read-only applicable
+                               if (worldChunk.Unpack_ReadOnly( ) == false) 
+                                       { 
+                                       Logger.Warning("Failed to unpack chunk: X{0} Y{1} Z{2}", key.X, targetChunkY, key.Y);
+                                       nullChunkCount++;
+                                       continue;
+                                       };
                                }
 
                                /*************** Chunk Entities Scanning *********************/
                                if (worldChunk.BlockEntities != null && worldChunk.BlockEntities.Count > 0)
                                {
                                        #if DEBUG
-                                       Logger.VerboseDebug("Scan pos.({0}) for BlockEntities# {1}", key, worldChunk.BlockEntities.Count);
+                                       Logger.VerboseDebug("Scan in Chunk:({0}, Y {2}):  found {1} BlockEntities", key, worldChunk.BlockEntities.Count,targetChunkY);
                                        #endif
 
                                        foreach (var blockEnt in worldChunk.BlockEntities)
                                        {
-                                               if (blockEnt.Value != null && blockEnt.Value.Block != null && BlockID_Designators.ContainsKey(blockEnt.Value.Block.BlockId))
+                                       var blockEntityPos = blockEnt.Key;
+                                       var blockEntity = blockEnt.Value;
+                                       if (blockEntityPos == null || blockEntity == null ) continue;
+                                       if (blockEntity.Block != null && !blockEntity.Block.IsMissing &&  BlockID_Designators.ContainsKey(blockEntity.Block.BlockId))
                                                {
                                                        var designator = BlockID_Designators[blockEnt.Value.Block.BlockId];
-                                                       designator.SpecialAction(ClientAPI, POIs, blockEnt.Value.Pos.Copy(), blockEnt.Value.Block);
+                                                       if (designator != null && designator.SpecialAction != null) designator.SpecialAction(ClientAPI, POIs, blockEnt.Value.Pos.Copy(), blockEnt.Value.Block);
                                                }
                                        }
                                }
@@ -634,8 +731,10 @@ namespace Automap
                                int X_index, Y_index, Z_index;
 
                                //First Chance fail-safe;
-                               if (worldChunk.Blocks == null || worldChunk.Blocks.Length <= 0) {
+                               if (worldChunk.MaybeBlocks == null || worldChunk.MaybeBlocks.Length <= 0) {
+                               #if DEBUG
                                Logger.VerboseDebug("WORLD chunk; Missing block DATA⁈ X{0} Y{1} Z{2} ⁈", key.X, targetChunkY, key.Y);
+                               #endif
                                nullChunkCount++;
                                continue;
                                }               
@@ -651,13 +750,15 @@ namespace Automap
                                                var indicie = MapUtil.Index3d(X_index, Y_index, Z_index, chunkSize, chunkSize);
 
                                                //'Last' Chance fail-safe;
-                                               if (worldChunk.Blocks == null || worldChunk.Blocks.Length <= 0) {
+                                               if (worldChunk.MaybeBlocks == null || worldChunk.MaybeBlocks.Length <= 0) {
+                                               #if DEBUG
                                                Logger.VerboseDebug("Processing Block: Missing block DATA⁈ X{0} Y{1} Z{2} ⁈", X_index, Y_index, Z_index);
+                                               #endif
                                                nullChunkCount++;
-                                               goto loop_bustout; ;
+                                               goto loop_bustout; 
                                                }
 
-                                               int aBlockId = worldChunk.Blocks[indicie];
+                                               int aBlockId = worldChunk.MaybeBlocks[indicie];
 
                                                if (aBlockId == 0 || AiryIdCodes.ContainsKey(aBlockId)) {//Airy blocks,,,
                                                chunkMeta.AirBlocks++;
@@ -685,29 +786,37 @@ namespace Automap
                                }
                                loop_bustout:;
                        }
+                       #if DEBUG
                        Logger.VerboseDebug("COLUMN X{0} Z{1}: {2}, processed.", key.X , key.Y, chunkTally + 1);
+                       #endif
                }
 
                private void UpdateEntityMetadata()
                {
-                       Logger.Debug("Presently {0} Entities", ClientAPI.World.LoadedEntities.Count);
-                       //Mabey scan only for 'new' entities by tracking ID in set?
-                       foreach (var loadedEntity in ClientAPI.World.LoadedEntities.ToArray())
-                       {
+               #if DEBUG
+               Logger.Debug("Presently {0} Entities", ClientAPI.World.LoadedEntities.Count);
+               #endif
+
+               var keyList = new long[ClientAPI.World.LoadedEntities.Keys.Count];
+               ClientAPI.World.LoadedEntities.Keys.CopyTo(keyList, 0);
 
+            //'ElementAt'; worse! instead; walk fixed list...
+               Entity loadedEntity;
+               foreach (var key in keyList)
+                       {
+                       if (ClientAPI.World.LoadedEntities.TryGetValue(key, out loadedEntity))
+                               {               
                                #if DEBUG
                                //Logger.VerboseDebug($"ENTITY: ({loadedEntity.Value.Code}) = #{loadedEntity.Value.EntityId} {loadedEntity.Value.State} {loadedEntity.Value.LocalPos}    <<<<<<<<<<<<");
                                #endif
 
-                               var dMatch = Entity_Designators.SingleOrDefault(se => se.Key.Equals(loadedEntity.Value.Code));
-                               if (dMatch.Value != null)
-                               {
-                                       dMatch.Value.SpecialAction(ClientAPI, this.EOIs, loadedEntity.Value.Pos.AsBlockPos.Copy(), loadedEntity.Value);
-                               }
-
+                               var dMatch = Entity_Designators.SingleOrDefault(se => se.Key.Equals(loadedEntity.Code));
+                               if (dMatch.Value != null) 
+                                       {
+                                       dMatch.Value.SpecialAction(ClientAPI, this.EOIs, loadedEntity.Pos.AsBlockPos.Copy( ), loadedEntity);
+                                       }
+                               }                               
                        }
-
-
                }
 
                private void AddNote(string notation)
@@ -739,7 +848,7 @@ namespace Automap
                                        if (CurrentState != cmdData.State)
                                        {
                                                CurrentState = cmdData.State;
-                                               AwakenCartographer(0.0f);
+                                               ThreadDecider(0.0f);
                                        }
                                        break;
 
@@ -748,11 +857,11 @@ namespace Automap
                                        AddNote(cmdData.Notation);
                                        break;
                        }
-                       #if DEBUG
+
                        ClientAPI.TriggerChatMessage($"Automap commanded to: {cmdData.State} ");
-                       #endif
+
                }
-               #endregion
+#endregion
 
                private AChunkRenderer InstantiateChosenRenderer(string rendererName )
                {
@@ -773,7 +882,7 @@ namespace Automap
                }
 
                return null;
-               }
+               }                       
        }
 
 }