X-Git-Url: http://git.osdn.net/view?p=automap%2Fautomap.git;a=blobdiff_plain;f=Automap%2FSubsystems%2FAutomapSystem.cs;h=32cda383711866c677e55b9120e486ac8f0a6232;hp=0a85be35dc22766c825afb6a52946231621a2cc8;hb=1c8849af29a916822885541c010ffc4b7bb90d5a;hpb=0fa1d15bd509262acce758c9a2fd3cc7b3552dc5 diff --git a/Automap/Subsystems/AutomapSystem.cs b/Automap/Subsystems/AutomapSystem.cs index 0a85be3..32cda38 100644 --- a/Automap/Subsystems/AutomapSystem.cs +++ b/Automap/Subsystems/AutomapSystem.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -8,12 +9,12 @@ using System.Text.RegularExpressions; using System.Threading; using Hjg.Pngcs; -using Hjg.Pngcs.Chunks; - +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,30 +25,33 @@ 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; } - private IChunkRenderer ChunkRenderer { get; set; } + private AChunkRenderer ChunkRenderer { get; set; } private JsonGenerator JsonGenerator { get; set; } internal const string _mapPath = @"Maps"; internal const string _chunkPath = @"Chunks"; + internal const uint editThreshold = 9; 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 const string plainMetadataFileName = @"map_metadata.txt"; private static Regex chunkShardRegex = new Regex(@"(?[\d]+)_(?[\d]+)\.png", RegexOptions.Singleline); - private ConcurrentDictionary columnCounter = new ConcurrentDictionary(3, 150); + private ConcurrentDictionary columnCounters = new ConcurrentDictionary(3, 150); 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 BlockID_Designators { get; private set; } internal Dictionary Entity_Designators { get; private set; } internal Dictionary RockIdCodes { get; private set; } + internal Dictionary AiryIdCodes { get; private set; } internal CommandType CurrentState { get; set; } //Run status, Chunks processed, stats, center of map.... @@ -68,11 +72,11 @@ namespace Automap this.ClientAPI = clientAPI; this.Logger = logger; chunkSize = ClientAPI.World.BlockAccessor.ChunkSize; - ClientAPI.Event.LevelFinalize += EngageAutomap; + configuration = config; + ClientAPI.Event.LevelFinalize += EngageAutomap; - //TODO:Choose which one from GUI - this.ChunkRenderer = new StandardRenderer(clientAPI, logger); + this.ChunkRenderer = InstantiateChosenRenderer(config.RendererName); //Listen on bus for commands ClientAPI.Event.RegisterEventBusListener(CommandListener, 1.0, AutomapSystem.AutomapCommandEventKey); @@ -81,7 +85,7 @@ namespace Automap if (configuration.Autostart) { CurrentState = CommandType.Run; - Logger.Debug("Autostart is Enabled."); + Logger.Notification("Autostart is Enabled."); } } @@ -118,32 +122,33 @@ 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(AwakenCartographer, 6000); + ClientAPI.Event.RegisterGameTickListener(ThreadDecider, 6000); } private void ChunkAChanging(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason) { - Vec2i topPosition = new Vec2i(chunkCoord.X, chunkCoord.Z); - - columnCounter.AddOrUpdate(topPosition, 1, (key, colAct) => colAct + 1); + Vec2i topPosition = new Vec2i(chunkCoord.X, chunkCoord.Z); + bool newOrEdit = (reason == EnumChunkDirtyReason.NewlyCreated || reason == EnumChunkDirtyReason.NewlyLoaded); + + columnCounters.AddOrUpdate(topPosition, + new ColumnCounter(chunkSize, newOrEdit, chunkCoord), + (chkPos, chkChng) => chkChng.Update(chunkCoord, chunkSize, newOrEdit) + ); + } - private void AwakenCartographer(float delayed) + /// + /// Cartographer Thread 'decider' + /// + /// called delay offset + 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)) { @@ -160,15 +165,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; } } - } @@ -179,14 +191,14 @@ namespace Automap try { - uint ejectedItem = 0; + ColumnCounter ejectedItem ; uint updatedChunks = 0; uint updatedPixels = 0; //-- Should dodge enumerator changing underfoot....at a cost. - if (!columnCounter.IsEmpty) + if (!columnCounters.IsEmpty) { - var tempSet = columnCounter.ToArray().OrderByDescending(kvp => kvp.Value); + var tempSet = columnCounters.ToArray().Where(cks => cks.Value.WeightedSum > editThreshold) .OrderByDescending(kvp => kvp.Value.WeightedSum); UpdateEntityMetadata(); foreach (var mostActiveCol in tempSet) @@ -195,9 +207,12 @@ namespace Automap if (mapChunk == null) { + //TODO: REVISIT THIS CHUNK! + #if DEBUG Logger.Warning("SKIP CHUNK: ({0}) - Map Chunk NULL!", mostActiveCol.Key); + #endif nullMapCount++; - columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); + columnCounters.TryRemove(mostActiveCol.Key, out ejectedItem); continue; } @@ -205,41 +220,40 @@ namespace Automap if (chunkTopMetadata.Contains(mostActiveCol.Key)) { chunkMeta = chunkTopMetadata[mostActiveCol.Key]; -#if DEBUG - Logger.VerboseDebug("Loaded chunk {0}", mostActiveCol.Key); -#endif + #if DEBUG + Logger.VerboseDebug("Loaded meta-chunk {0}", mostActiveCol.Key); + #endif } else { chunkMeta = CreateColumnMetadata(mostActiveCol, mapChunk); -#if DEBUG - Logger.VerboseDebug("Created chunk {0}", mostActiveCol.Key); -#endif + #if DEBUG + Logger.VerboseDebug("Created meta-chunk {0}", mostActiveCol.Key); + #endif } ProcessChunkBlocks(mostActiveCol.Key, mapChunk, ref chunkMeta); + mostActiveCol.Value.SetCutoff(chunkMeta.YMax / chunkSize); - PngWriter pngWriter = SetupPngImage(mostActiveCol.Key, ref chunkMeta); - ChunkRenderer.GenerateChunkPngShard(mostActiveCol.Key, mapChunk, chunkMeta, pngWriter, out updatedPixels); + ChunkRenderer.SetupPngImage(mostActiveCol.Key, path, _chunkPath, ref chunkMeta); + ChunkRenderer.GenerateChunkPngShard(mostActiveCol.Key, mapChunk, chunkMeta, ref chunkTopMetadata, out updatedPixels); if (updatedPixels > 0) { -#if DEBUG - Logger.VerboseDebug("Wrote chunk shard: ({0}) - Edits#:{1}, Pixels#:{2}", mostActiveCol.Key, mostActiveCol.Value, updatedPixels); -#endif + #if DEBUG + Logger.VerboseDebug("Wrote top-chunk shard: ({0}) - Weight:{1}, Pixels#:{2}", mostActiveCol.Key, mostActiveCol.Value, updatedPixels); + #endif updatedChunks++; chunkTopMetadata.Update(chunkMeta); - columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); + columnCounters.TryRemove(mostActiveCol.Key, out ejectedItem); } else { - columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); -#if DEBUG - Logger.VerboseDebug("Un-painted chunk: ({0}) ", mostActiveCol.Key); -#endif + columnCounters.TryRemove(mostActiveCol.Key, out ejectedItem); + #if DEBUG + Logger.VerboseDebug("Un-painted chunk shard: ({0}) ", mostActiveCol.Key); + #endif } } - //Cleanup persisted Metadata... - chunkTopMetadata.ClearMetadata(); } UpdateStatus(this.updatedChunksTotal, this.nullChunkCount, updatedChunks); @@ -250,8 +264,17 @@ namespace Automap updatedChunksTotal += updatedChunks; JsonGenerator.GenerateJSONMetadata(chunkTopMetadata, startChunkColumn, POIs, EOIs, RockIdCodes); updatedChunks = 0; + + //Cleanup in-memory Metadata... + chunkTopMetadata.ClearMetadata( ); } + #if DEBUG + Logger.VerboseDebug("Clearing Column Counters of: {0} non-written shards", columnCounters.Count); + #endif + + columnCounters.Clear( ); + //Then sleep until interupted again, and repeat #if DEBUG Logger.VerboseDebug("Thread '{0}' about to sleep indefinitely.", Thread.CurrentThread.Name); @@ -280,6 +303,7 @@ namespace Automap Logger.VerboseDebug("Thread '{0}' executing finally block.", Thread.CurrentThread.Name); #endif PersistPointsData(); + Write_PlainMetadata( ); } } @@ -321,41 +345,60 @@ namespace Automap this.Entity_Designators = new Dictionary(); this.RockIdCodes = Helpers.ArbitrarytBlockIdHunter(ClientAPI, new AssetLocation(GlobalConstants.DefaultDomain, "rock-"), EnumBlockMaterial.Stone); - //Add special marker types for BlockID's of "Interest", overwrite colour, and method + 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 || 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 - Reload_POI_Designators(); + //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); } @@ -373,17 +416,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(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(eoiFile, this.EOIs); + eoiFile.Flush(true); } } @@ -392,31 +437,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(); @@ -425,9 +472,36 @@ namespace Automap } - private ColumnMeta CreateColumnMetadata(KeyValuePair mostActiveCol, IMapChunk mapChunk) + private void Write_PlainMetadata( ) + { + string metaPath = Path.Combine(path, plainMetadataFileName); + + using (var metaDataFile = File.Open(metaPath,FileMode.Create)) { + using (var mdWriter = new StreamWriter(metaDataFile, Encoding.ASCII)) + { + mdWriter.WriteLine("WorldSeed {0}", ClientAPI.World.Seed); + 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("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( ); + } + } + } + + + private ColumnMeta CreateColumnMetadata(KeyValuePair mostActiveCol, IMapChunk mapChunk) { - ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), ClientAPI, (byte) chunkSize); + ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), ClientAPI, (byte) chunkSize, (ClientAPI.World.BlockAccessor.MapSizeY / chunkSize)); BlockPos equivBP = new BlockPos(mostActiveCol.Key.X * chunkSize, mapChunk.YMax, mostActiveCol.Key.Y * chunkSize); @@ -526,29 +600,7 @@ namespace Automap } - private PngWriter SetupPngImage(Vec2i coord, ref ColumnMeta metadata) - { - ImageInfo imageInf = new ImageInfo(chunkSize, chunkSize, 8, false); - - string filename = $"{coord.X}_{coord.Y}.png"; - filename = Path.Combine(path, _chunkPath ,filename); - - PngWriter pngWriter = FileHelper.CreatePngWriter(filename, imageInf, true); - PngMetadata meta = pngWriter.GetMetadata(); - meta.SetTimeNow(); - meta.SetText("Chunk_X", coord.X.ToString("D")); - meta.SetText("Chunk_Y", coord.Y.ToString("D")); - //Setup specialized meta-data PNG chunks here... - PngMetadataChunk pngChunkMeta = new PngMetadataChunk(pngWriter.ImgInfo) - { - ChunkMetadata = metadata - }; - pngWriter.GetChunksList().Queue(pngChunkMeta); - pngWriter.CompLevel = 5;// 9 is the maximum compression but thats too high for the small benefit it gives - pngWriter.CompressionStrategy = Hjg.Pngcs.Zlib.EDeflateCompressStrategy.Huffman; - return pngWriter; - } /// /// Does the heavy lifting of Scanning columns of chunks - scans for BlockEntity, creates Heightmap and stats... @@ -558,109 +610,145 @@ namespace Automap /// Chunk metadata private void ProcessChunkBlocks(Vec2i key, IMapChunk mapChunk, ref ColumnMeta chunkMeta) { + int targetChunkY = mapChunk.YMax / chunkSize;//Surface ish... + byte chunkTally = 0; + + #if DEBUG + Logger.VerboseDebug("Start col @ X{0} Y{1} Z{2} !", key.X, targetChunkY, key.Y); + #endif - int targetChunkY = mapChunk.YMax / chunkSize;//Surface ... - for (; targetChunkY > 0; targetChunkY--) + chunkMeta.ResetMetadata(ClientAPI.World.BlockAccessor.MapSizeY); + + for (; targetChunkY > 0; targetChunkY--) { - WorldChunk chunkData = ClientAPI.World.BlockAccessor.GetChunk(key.X, targetChunkY, key.Y) as WorldChunk; + WorldChunk worldChunk = ClientAPI.World.BlockAccessor.GetChunk(key.X, targetChunkY, key.Y) as WorldChunk; - if (chunkData == null || chunkData.BlockEntities == null) + if (worldChunk == null || worldChunk.BlockEntities == null) { -#if DEBUG - Logger.VerboseDebug("Chunk null or empty X{0} Y{1} Z{2}", key.X, targetChunkY, key.Y); -#endif + #if DEBUG + Logger.VerboseDebug("WORLD chunk: null or empty X{0} Y{1} Z{2} !", key.X, targetChunkY, key.Y); + #endif nullChunkCount++; continue; } + if (worldChunk.IsPacked()) + { + #if DEBUG + Logger.VerboseDebug("WORLD chunk: Compressed: X{0} Y{1} Z{2}", key.X, targetChunkY, key.Y); + #endif + worldChunk.Unpack( );//RESEARCH: Thread Unsafe? + } + /*************** Chunk Entities Scanning *********************/ - if (chunkData.BlockEntities != null && chunkData.BlockEntities.Count > 0) + if (worldChunk.BlockEntities != null && worldChunk.BlockEntities.Count > 0) { -#if DEBUG - Logger.VerboseDebug("Surface@ {0} = BlockEntities: {1}", key, chunkData.BlockEntities.Count); -#endif + #if DEBUG + Logger.VerboseDebug("Scan pos.({0}) for BlockEntities# {1}", key, worldChunk.BlockEntities.Count); + #endif - foreach (var blockEnt in chunkData.BlockEntities) + foreach (var blockEnt in worldChunk.BlockEntities) { - if (blockEnt.Value != null && blockEnt.Value.Block != null && BlockID_Designators.ContainsKey(blockEnt.Value.Block.BlockId)) + if (blockEnt.Key != null && blockEnt.Value != null && blockEnt.Value.Block != null && BlockID_Designators.ContainsKey(blockEnt.Value.Block.BlockId)) { var designator = BlockID_Designators[blockEnt.Value.Block.BlockId]; - designator.SpecialAction(ClientAPI, POIs, blockEnt.Value.Pos.Copy(), blockEnt.Value.Block); + designator?.SpecialAction(ClientAPI, POIs, blockEnt.Value.Pos.Copy(), blockEnt.Value.Block); } } } + /********************* Chunk/Column BLOCKs scanning ****************/ //Heightmap, Stats, block tally - chunkData.Unpack(); - - //int X_index, Y_index, Z_index; - - //Ensure ChunkData Metadata fields arn't null...due to being tossed out - //if (chunkMeta.HeightMap == null) { chunkMeta.HeightMap = new ushort[chunkSize, chunkSize]; } - //if (chunkMeta.RockRatio == null) { chunkMeta.RockRatio = new Dictionary(10); } - - //for (Y_index = 0; Y_index < chunkSize - 1; Y_index++) - //{ - // for (Z_index = 0; Z_index < chunkSize - 1; Z_index++) - // { - // for (X_index = 0; X_index < chunkSize - 1; X_index++) - // { - // /* Encode packed indicie - // (y * chunksize + z) * chunksize + x - // */ - // var indicie = Helpers.ChunkBlockIndicie16(X_index, Y_index, Z_index); - // int aBlockId = chunkData.Blocks[indicie]; - - // if (aBlockId == 0) - // {//Air - // chunkMeta.AirBlocks++; - // continue; - // } - - // if (RockIdCodes.ContainsKey(aBlockId)) - // { - // if (chunkMeta.RockRatio.ContainsKey(aBlockId)) - // chunkMeta.RockRatio[aBlockId]++; - // else - // chunkMeta.RockRatio.Add(aBlockId, 1); - // } - - // chunkMeta.NonAirBlocks++; - - // //Heightmap - // //if (chunkMeta.HeightMap[X_index, Z_index] == 0) - // //{ - - // // chunkMeta.HeightMap[X_index, Z_index] = (ushort) (Y_index + (targetChunkY * chunkSize)); - // //} - // } - // } - - //} + int X_index, Y_index, Z_index; + + //First Chance fail-safe; + if (worldChunk.Blocks == null || worldChunk.Blocks.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; + } + + chunkMeta.ColumnPresense[targetChunkY] = true; + chunkTally++; + for (Y_index = 0; Y_index < chunkSize; Y_index++) + { + for (Z_index = 0; Z_index < chunkSize; Z_index++) + { + for (X_index = 0; X_index < chunkSize; X_index++) + { + 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 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; + } + + int aBlockId = worldChunk.Blocks[indicie]; + + if (aBlockId == 0 || AiryIdCodes.ContainsKey(aBlockId)) {//Airy blocks,,, + chunkMeta.AirBlocks++; + continue; + } + + if (RockIdCodes.ContainsKey(aBlockId)) { + if (chunkMeta.RockRatio.ContainsKey(aBlockId)) + chunkMeta.RockRatio[aBlockId]++; + else + chunkMeta.RockRatio.Add(aBlockId, 1); + } + + chunkMeta.NonAirBlocks++; + + ushort localHeight = ( ushort )(Y_index + (targetChunkY * chunkSize)); + //Heightmap - Need to ignore Grass & Snow + if (localHeight > chunkMeta.HeightMap[X_index, Z_index]) + { + chunkMeta.HeightMap[X_index, Z_index] = localHeight; + if (localHeight > chunkMeta.YMax) chunkMeta.YMax = localHeight; + } + } + } + } + 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 -#if DEBUG - //Logger.VerboseDebug($"ENTITY: ({loadedEntity.Value.Code}) = #{loadedEntity.Value.EntityId} {loadedEntity.Value.State} {loadedEntity.Value.LocalPos} <<<<<<<<<<<<"); -#endif + var keyList = new long[ClientAPI.World.LoadedEntities.Keys.Count]; + ClientAPI.World.LoadedEntities.Keys.CopyTo(keyList, 0); - 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); - } + //'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.Code)); + if (dMatch.Value != null) + { + dMatch.Value.SpecialAction(ClientAPI, this.EOIs, loadedEntity.Pos.AsBlockPos.Copy( ), loadedEntity); + } + } } - - } private void AddNote(string notation) @@ -692,7 +780,7 @@ namespace Automap if (CurrentState != cmdData.State) { CurrentState = cmdData.State; - AwakenCartographer(0.0f); + ThreadDecider(0.0f); } break; @@ -701,12 +789,32 @@ namespace Automap AddNote(cmdData.Notation); break; } -#if DEBUG + ClientAPI.TriggerChatMessage($"Automap commanded to: {cmdData.State} "); -#endif + + } +#endregion + + private AChunkRenderer InstantiateChosenRenderer(string rendererName ) + { + Logger.VerboseDebug("Using '{0}' style Shard Renderer", rendererName); + switch (rendererName) + { + case StandardRenderer.Name: + return new StandardRenderer(ClientAPI, Logger, this.configuration.SeasonalColors); + + case AlternateRenderer.Name: + return new AlternateRenderer(ClientAPI, Logger, this.configuration.SeasonalColors); + + case FlatRenderer.Name: + return new FlatRenderer(ClientAPI, Logger, this.configuration.SeasonalColors); + + default: + throw new ArgumentOutOfRangeException("rendererName",rendererName,"That value isn't supported or known..."); } - #endregion + return null; + } } }