X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=Automap%2FSubsystems%2FAutomapSystem.cs;h=f61fef5769ac9fc3fb4e1b674549d1c4b9206cd1;hb=983e2ab853a3288297ed32d6fb46b8655041c009;hp=53a785f2bbd8582d08c78dc1f69dd0412ffad9e3;hpb=cdc29d29d788f9126faee33e48c4f65968b49702;p=automap%2Fautomap.git diff --git a/Automap/Subsystems/AutomapSystem.cs b/Automap/Subsystems/AutomapSystem.cs index 53a785f..f61fef5 100644 --- a/Automap/Subsystems/AutomapSystem.cs +++ b/Automap/Subsystems/AutomapSystem.cs @@ -1,18 +1,16 @@ using System; +using System.Collections; 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; using Hjg.Pngcs; -using Hjg.Pngcs.Chunks; -using Newtonsoft.Json; +using ProtoBuf; using Vintagestory.API.Client; using Vintagestory.API.Common; @@ -26,29 +24,35 @@ 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; } - private const string _mapPath = @"Maps"; - private const string _chunkPath = @"Chunks"; + internal const string _mapPath = @"Maps"; + internal const string _chunkPath = @"Chunks"; private const string _domain = @"automap"; private const string chunkFile_filter = @"*_*.png"; - private static Regex chunkShardRegex = new Regex(@"(?[\d]+)_(?[\d]+).png", RegexOptions.Singleline); + private const string poiFileName = @"poi_binary"; + private const string eoiFileName = @"eoi_binary"; + private const string pointsTsvFileName = @"points_of_interest.tsv"; + private static Regex chunkShardRegex = new Regex(@"(?[\d]+)_(?[\d]+)\.png", RegexOptions.Singleline); private ConcurrentDictionary columnCounter = new ConcurrentDictionary(3, 150); private ColumnsMetadata chunkTopMetadata; private PointsOfInterest POIs = new PointsOfInterest(); private EntitiesOfInterest EOIs = new EntitiesOfInterest(); - private string jsonPreBuilt; 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.... - private uint nullChunkCount, updatedChunksTotal; + private uint nullChunkCount, nullMapCount, updatedChunksTotal; private Vec2i startChunkColumn; private readonly int chunkSize; @@ -59,7 +63,6 @@ namespace Automap public static string AutomapStatusEventKey = @"AutomapStatus"; public static string AutomapCommandEventKey = @"AutomapCommand"; - //PersistedConfiguration cachedConfiguration; public AutomapSystem(ICoreClientAPI clientAPI, ILogger logger, PersistedConfiguration config) { @@ -69,14 +72,13 @@ namespace Automap ClientAPI.Event.LevelFinalize += EngageAutomap; configuration = config; - - //TODO:Choose which one from GUI - this.ChunkRenderer = new StandardRenderer(clientAPI, logger); + this.ChunkRenderer = new StandardRenderer(clientAPI, logger, this.configuration.SeasonalColors); //Listen on bus for commands ClientAPI.Event.RegisterEventBusListener(CommandListener, 1.0, AutomapSystem.AutomapCommandEventKey); + if (configuration.Autostart) { CurrentState = CommandType.Run; @@ -91,7 +93,9 @@ namespace Automap { path = ClientAPI.GetOrCreateDataPath(_mapPath); path = ClientAPI.GetOrCreateDataPath(Path.Combine(path, "World_" + ClientAPI.World.Seed));//Add name of World too...'ServerApi.WorldManager.CurrentWorldName' - + ClientAPI.GetOrCreateDataPath(Path.Combine(path, _chunkPath)); + + JsonGenerator = new JsonGenerator(ClientAPI, Logger, path); string mapFilename = Path.Combine(path, "automap.html"); StreamWriter outputText = new StreamWriter(File.Open(mapFilename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)); @@ -100,12 +104,9 @@ 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)); + startChunkColumn = new Vec2i((ClientAPI.World.Player.Entity.Pos.AsBlockPos.X / chunkSize), (ClientAPI.World.Player.Entity.Pos.AsBlockPos.Z / chunkSize)); chunkTopMetadata = new ColumnsMetadata(startChunkColumn); - Logger.Notification("AUTOMAP Start {0}", startChunkColumn); Reload_Metadata(); @@ -118,14 +119,27 @@ 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); } 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); + + //TODO: Track Y Chunk - Column, surface chunks being more important + //Only NEW/LOADED chunks unless edits > N + //if (reason == EnumChunkDirtyReason.NewlyCreated || reason == EnumChunkDirtyReason.NewlyLoaded) + //{ + columnCounter.AddOrUpdate(topPosition, 1, (key, colAct) => colAct + 1); + //} } private void AwakenCartographer(float delayed) @@ -137,11 +151,11 @@ namespace Automap Logger.VerboseDebug("Cartographer re-trigger from [{0}]", cartographer_thread.ThreadState); #endif - if (cartographer_thread.ThreadState.HasFlag(System.Threading.ThreadState.Unstarted)) + if (cartographer_thread.ThreadState.HasFlag(ThreadState.Unstarted)) { cartographer_thread.Start(); } - else if (cartographer_thread.ThreadState.HasFlag(System.Threading.ThreadState.WaitSleepJoin)) + else if (cartographer_thread.ThreadState.HasFlag(ThreadState.WaitSleepJoin)) { //Time to (re)write chunk shards cartographer_thread.Interrupt(); @@ -152,7 +166,13 @@ namespace Automap } else if (CurrentState == CommandType.Snapshot) { - //TODO: Snapshot generator second thread... + if (snapshotThread.ThreadState.HasFlag(ThreadState.Unstarted)) + { + snapshotThread.Start(); + } else if (snapshotThread.ThreadState.HasFlag(ThreadState.WaitSleepJoin)) + { + snapshotThread.Interrupt(); + } } } @@ -181,8 +201,9 @@ namespace Automap if (mapChunk == null) { + //TODO: REVISIT THIS CHUNK! Logger.Warning("SKIP CHUNK: ({0}) - Map Chunk NULL!", mostActiveCol.Key); - nullChunkCount++; + nullMapCount++; columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); continue; } @@ -191,23 +212,27 @@ namespace Automap if (chunkTopMetadata.Contains(mostActiveCol.Key)) { chunkMeta = chunkTopMetadata[mostActiveCol.Key]; + #if DEBUG + Logger.VerboseDebug("Loaded meta-chunk {0}", mostActiveCol.Key); + #endif } else { chunkMeta = CreateColumnMetadata(mostActiveCol, mapChunk); + #if DEBUG + Logger.VerboseDebug("Created meta-chunk {0}", mostActiveCol.Key); + #endif } - ProcessChunkBlocks(mostActiveCol.Key, mapChunk, ref chunkMeta); - PngWriter pngWriter = SetupPngImage(mostActiveCol.Key, 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}) - Edits#:{1}, Pixels#:{2}", mostActiveCol.Key, mostActiveCol.Value, updatedPixels); + #endif updatedChunks++; chunkTopMetadata.Update(chunkMeta); columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); @@ -215,9 +240,10 @@ namespace Automap else { columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); - Logger.VerboseDebug("Un-painted chunk: ({0}) ", mostActiveCol.Key); + #if DEBUG + Logger.VerboseDebug("Un-painted chunk shard: ({0}) ", mostActiveCol.Key); + #endif } - } } @@ -227,32 +253,65 @@ namespace Automap { //What about chunk updates themselves; a update bitmap isn't kept... updatedChunksTotal += updatedChunks; - GenerateJSONMetadata(); + JsonGenerator.GenerateJSONMetadata(chunkTopMetadata, startChunkColumn, POIs, EOIs, RockIdCodes); updatedChunks = 0; + + //Cleanup in-memory Metadata... + chunkTopMetadata.ClearMetadata( ); } //Then sleep until interupted again, and repeat - +#if DEBUG Logger.VerboseDebug("Thread '{0}' about to sleep indefinitely.", Thread.CurrentThread.Name); - +#endif Thread.Sleep(Timeout.Infinite); } catch (ThreadInterruptedException) { +#if DEBUG Logger.VerboseDebug("Thread '{0}' interupted [awoken]", Thread.CurrentThread.Name); +#endif goto wake; } catch (ThreadAbortException) { +#if DEBUG Logger.VerboseDebug("Thread '{0}' aborted.", Thread.CurrentThread.Name); - +#endif } finally { +#if DEBUG Logger.VerboseDebug("Thread '{0}' executing finally block.", Thread.CurrentThread.Name); +#endif + PersistPointsData(); + } + } + + private void Snap() + { + snapshotTake: +#if DEBUG + Logger.VerboseDebug("Snapshot started"); +#endif + try + { + snapshot.Take(); +#if DEBUG + Logger.VerboseDebug("Snapshot sleeping"); +#endif + CurrentState = CommandType.Run; + Thread.Sleep(Timeout.Infinite); + } + catch (ThreadInterruptedException) + { +#if DEBUG + Logger.VerboseDebug("Snapshot intertupted"); +#endif + goto snapshotTake; } } @@ -270,8 +329,15 @@ 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 + 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(); } @@ -302,109 +368,81 @@ namespace Automap this.Entity_Designators.Add(match.Code, designator); } - - //EntityProperties props = ClientAPI.World.GetEntityType(designator.Pattern); } } + + /// - /// Generates the JSON Metadata. (in Map object format ) + /// Store Points/Entity of Interest /// - private void GenerateJSONMetadata() + private void PersistPointsData() { - string jsonFilename = Path.Combine(path, "Metadata.js"); - - StreamWriter stream = new StreamWriter(jsonFilename, false, Encoding.UTF8); + //POI and EOI raw dump files ~ WRITE em! + //var poiRawFile = File. + string poiPath = Path.Combine(path, poiFileName); + string eoiPath = Path.Combine(path, eoiFileName); - using (stream) + if (this.POIs.Count > 0) { - Stopwatch stopWatch = new Stopwatch(); - stopWatch.Start(); - - stream.Write(jsonPreBuilt); - - stream.Write("ViewFrame.chunks.genTime='{0}';", - DateTimeOffset.UtcNow - ); - - stream.Write("ViewFrame.chunks.startCoords=[{0},{1}];", - startChunkColumn.X, startChunkColumn.Y - ); - - 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" - stream.Write("ViewFrame.chunks.chunkMetadata=new Map(["); - - foreach (var shard in chunkTopMetadata) + using (var poiFile = File.OpenWrite(poiPath)) { - shard.Write(stream, ClientAPI); - stream.Write(","); + Serializer.Serialize(poiFile, this.POIs); } - stream.Write("]);");// Close constructor of Map (chunkMetadata) - - - stream.Write("ViewFrame.chunks.pointsOfInterest=new Map(["); + } - foreach (var poi in POIs) + if (this.EOIs.Count > 0) + { + using (var eoiFile = File.OpenWrite(eoiPath)) { - poi.Write(stream, ClientAPI); - stream.Write(","); + Serializer.Serialize(eoiFile, this.EOIs); } + } - stream.Write("]);"); + //Create Easy to Parse TSV file for tool/human use.... + string pointsTsvPath = Path.Combine(path, pointsTsvFileName); - stream.Write("ViewFrame.chunks.entitiesOfInterest=new Map(["); - foreach (var eoi in EOIs) + using (var tsvWriter = new StreamWriter(pointsTsvPath, false, Encoding.UTF8)) + { + tsvWriter.WriteLine("Name\tDescription\tLocation\tTime\tDestination"); + foreach (var point in this.POIs) { - eoi.Write(stream, ClientAPI); - stream.Write(","); + tsvWriter.Write(point.Name + "\t"); + var notes = point.Notes + .Replace("\n", "\\n") + .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.WriteLine(); } - stream.Write("]);\n"); - - //stream.Write("//============= BlockID's for Rockmap / Rock-ratios ==============="); - - //stream.Write("ViewFrame.chunks.rockLookup=new Map(["); - - //foreach (var entry in RockIdCodes) - //{ - // var block = ClientAPI.World.GetBlock(entry.Key); - - // stream.Write("["); - // stream.Write("'{0},", block.Code.Path); - - // stream.Write("["); - // stream.Write("'{0}',", entry.Value); - - // stream.Write("{0},", Lang.GetUnformatted(block.Code.Path)); - // //Color? - - // stream.Write("]],"); - //} - - //stream.Write("]);\n"); - - stream.Flush(); - - stopWatch.Stop(); - Logger.Debug("Run time of chunk write {0}", stopWatch.Elapsed); + foreach (var entity in this.EOIs) + { + tsvWriter.Write(entity.Name + "\t"); + var notes = entity.Notes + .Replace("\n", "\\n") + .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.WriteLine(); + } + tsvWriter.WriteLine(); + tsvWriter.Flush(); } } - private ColumnMeta CreateColumnMetadata(KeyValuePair mostActiveCol, IMapChunk mapChunk) { - ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), (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); @@ -421,90 +459,89 @@ namespace Automap /// The metadata. private void Reload_Metadata() { - var worldmapDir = new DirectoryInfo(path); + var shardsDir = new DirectoryInfo( Path.Combine(path, _chunkPath) ); - if (worldmapDir.Exists) + if (!shardsDir.Exists) { + #if DEBUG + Logger.VerboseDebug("Could not open world map (shards) directory"); + #endif + return; + } + var shardFiles = shardsDir.GetFiles(chunkFile_filter); - var files = worldmapDir.GetFiles(chunkFile_filter); + if (shardFiles.Length > 0) + { + #if DEBUG + Logger.VerboseDebug("Metadata reloading from {0} shards", shardFiles.Length); + #endif - if (files.Length > 0) + foreach (var shardFile in shardFiles) { -#if DEBUG - Logger.VerboseDebug("{0} Existing world chunk shards", files.Length); -#endif - foreach (var shardFile in files) - { + if (shardFile.Length < 1024) continue; + var result = chunkShardRegex.Match(shardFile.Name); + if (!result.Success) continue; - if (shardFile.Length < 1024) continue; - var result = chunkShardRegex.Match(shardFile.Name); - if (result.Success) - { - int X_chunk_pos = int.Parse(result.Groups["X"].Value); - int Z_chunk_pos = int.Parse(result.Groups["Z"].Value); + int X_chunk_pos = int.Parse(result.Groups["X"].Value); + int Z_chunk_pos = int.Parse(result.Groups["Z"].Value); - try - { - using (var fileStream = shardFile.OpenRead()) - { - - PngReader pngRead = new PngReader(fileStream); - pngRead.ReadSkippingAllRows(); - pngRead.End(); - //Parse PNG chunks for METADATA in shard - PngMetadataChunk metadataFromPng = pngRead.GetChunksList().GetById1(PngMetadataChunk.ID) as PngMetadataChunk; - - chunkTopMetadata.Add(metadataFromPng.ChunkMetadata); - } + try + { + using (var fileStream = shardFile.OpenRead()) + { - } - catch (PngjException someEx) - { - Logger.Error("PNG Corruption file '{0}' - Reason: {1}", shardFile.Name, someEx); - continue; - } + PngReader pngRead = new PngReader(fileStream); + pngRead.ReadSkippingAllRows(); + pngRead.End(); + //Parse PNG chunks for METADATA in shard + PngMetadataChunk metadataFromPng = pngRead.GetChunksList().GetById1(PngMetadataChunk.ID) as PngMetadataChunk; + var column = metadataFromPng.ChunkMetadata; + if (column.PrettyLocation == null) + column = column.Reload(ClientAPI); + chunkTopMetadata.Add(column); } } + catch (PngjException someEx) + { + Logger.Error("PNG Corruption file '{0}' - Reason: {1}", shardFile.Name, someEx); + continue; + } + catch (ProtoException protoEx) + { + Logger.Error("ProtoBuf invalid! file:'{0}' - Reason: {1}", shardFile.Name, protoEx); + continue; + } } + } + //POI and EOI raw dump files ~ reload em! + //var poiRawFile = File. + string poiPath = Path.Combine(path, poiFileName); + string eoiPath = Path.Combine(path, eoiFileName); - } - else + if (File.Exists(poiPath)) { -#if DEBUG - Logger.VerboseDebug("Could not open world map directory"); -#endif + using (var poiFile = File.OpenRead(poiPath)) + { + this.POIs = Serializer.Deserialize(poiFile); + Logger.VerboseDebug("Reloaded {0} POIs from file.", this.POIs.Count); + } } - + if (File.Exists(eoiPath)) + { + using (var eoiFile = File.OpenRead(eoiPath)) + { + this.EOIs = Serializer.Deserialize(eoiFile); + Logger.VerboseDebug("Reloaded {0} EOIs from file.", this.EOIs.Count); + } + } } - private PngWriter SetupPngImage(Vec2i coord, ColumnMeta metadata) - { - ImageInfo imageInf = new ImageInfo(chunkSize, chunkSize, 8, false); - - string filename = $"{coord.X}_{coord.Y}.png"; - filename = Path.Combine(path, 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 = 9;// 9 is the maximum compression - 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... @@ -514,78 +551,109 @@ 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 + + chunkMeta.ResetMetadata(ClientAPI.World.BlockAccessor.MapSizeY); - int targetChunkY = mapChunk.YMax / chunkSize;//Surface ... - for (; targetChunkY > 0; targetChunkY--) + for (; targetChunkY > 0; targetChunkY--) { - WorldChunk chunkData = ClientAPI.World.BlockAccessor.GetChunk(key.X, targetChunkY, key.Y) as WorldChunk; - if (chunkData == null || chunkData.BlockEntities == null) + WorldChunk worldChunk = ClientAPI.World.BlockAccessor.GetChunk(key.X, targetChunkY, key.Y) as WorldChunk; + + 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()) + { + Logger.VerboseDebug("WORLD chunk: Compressed: X{0} Y{1} Z{2}", key.X, targetChunkY, key.Y); + worldChunk.Unpack( );//RESEARCH: Thread Unsafe? + } + /*************** Chunk Entities Scanning *********************/ - if (chunkData.BlockEntities != null && chunkData.BlockEntities.Length > 0) + if (worldChunk.BlockEntities != null && worldChunk.BlockEntities.Count > 0) { -#if DEBUG - Logger.VerboseDebug("Surface@ {0} = BlockEntities: {1}", key, chunkData.BlockEntities.Length); -#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 != null && blockEnt.Block != null && BlockID_Designators.ContainsKey(blockEnt.Block.BlockId)) + if (blockEnt.Value != null && blockEnt.Value.Block != null && BlockID_Designators.ContainsKey(blockEnt.Value.Block.BlockId)) { - var designator = BlockID_Designators[blockEnt.Block.BlockId]; - designator.SpecialAction(ClientAPI, POIs, blockEnt.Pos.Copy(), blockEnt.Block); + var designator = BlockID_Designators[blockEnt.Value.Block.BlockId]; + 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; - X_index = Y_index = Z_index = 0; - do + //First Chance fail-safe; + if (worldChunk.Blocks == null || worldChunk.Blocks.Length <= 0) { + Logger.VerboseDebug("WORLD chunk; Missing block DATA⁈ X{0} Y{1} Z{2} ⁈", key.X, targetChunkY, key.Y); + nullChunkCount++; + continue; + } + + chunkMeta.ColumnPresense[targetChunkY] = true; + chunkTally++; + for (Y_index = 0; Y_index < chunkSize; Y_index++) { - do + for (Z_index = 0; Z_index < chunkSize; Z_index++) { - do + for (X_index = 0; X_index < chunkSize; 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; - } + var indicie = MapUtil.Index3d(X_index, Y_index, Z_index, chunkSize, chunkSize); + + //'Last' Chance fail-safe; + if (worldChunk.Blocks == null || worldChunk.Blocks.Length <= 0) { + Logger.VerboseDebug("Processing Block: Missing block DATA⁈ X{0} Y{1} Z{2} ⁈", X_index, Y_index, Z_index); + nullChunkCount++; + goto loop_bustout; ; + } + + int aBlockId = worldChunk.Blocks[indicie]; - if (RockIdCodes.ContainsKey(aBlockId)) + 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]) { - if (chunkMeta.RockRatio.ContainsKey(aBlockId)) { chunkMeta.RockRatio[aBlockId]++; } else { chunkMeta.RockRatio.Add(aBlockId, 1); } + chunkMeta.HeightMap[X_index, Z_index] = localHeight; + if (localHeight > chunkMeta.YMax) chunkMeta.YMax = localHeight; } - - chunkMeta.NonAirBlocks++; } - while (X_index++ < (chunkSize - 1)); - X_index = 0; } - while (Z_index++ < (chunkSize - 1)); - Z_index = 0; } - while (Y_index++ < (chunkSize - 1)); - + loop_bustout:; } + Logger.VerboseDebug("COLUMN X{0} Z{1}: {2}, processed.", key.X , key.Y, chunkTally + 1); } private void UpdateEntityMetadata() @@ -595,15 +663,14 @@ 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) { - Logger.Chat("Entity designator hit!"); - dMatch.Value.SpecialAction(ClientAPI, this.EOIs, loadedEntity.Value.LocalPos.AsBlockPos.Copy(), loadedEntity.Value); + dMatch.Value.SpecialAction(ClientAPI, this.EOIs, loadedEntity.Value.Pos.AsBlockPos.Copy(), loadedEntity.Value); } } @@ -615,9 +682,10 @@ namespace Automap { var playerNodePoi = new PointOfInterest() { - Location = ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Copy(), + Name = "Note", + Location = ClientAPI.World.Player.Entity.Pos.AsBlockPos.Copy(), Notes = notation, - Timestamp = DateTimeOffset.UtcNow, + Timestamp = DateTime.UtcNow, }; this.POIs.AddReplace(playerNodePoi); @@ -627,112 +695,33 @@ namespace Automap private void CommandListener(string eventName, ref EnumHandling handling, IAttribute data) { - Logger.VerboseDebug("MsgBus RX: AutomapCommandMsg: {0}", data.ToJsonToken()); + //Logger.VerboseDebug("MsgBus RX: AutomapCommandMsg: {0}", data.ToJsonToken()); CommandData cmdData = data as CommandData; - - if (CurrentState != CommandType.Snapshot) + switch (cmdData.State) { - switch (cmdData.State) - { - case CommandType.Run: + case CommandType.Run: + case CommandType.Stop: + case CommandType.Snapshot: + if (CurrentState != cmdData.State) + { CurrentState = cmdData.State; AwakenCartographer(0.0f); - break; - - case CommandType.Stop: - CurrentState = cmdData.State; - break; - - case CommandType.Snapshot: - CurrentState = CommandType.Stop; - //Snapshot starts a second thread/process... - - break; - - case CommandType.Notation: - //Add to POI list where player location - AddNote(cmdData.Notation); - break; - } - - } + } + break; - if (CurrentState != cmdData.State) - { - CurrentState = cmdData.State; - AwakenCartographer(0.0f); + case CommandType.Notation: + //Add to POI list where player location + AddNote(cmdData.Notation); + break; } - #if DEBUG - Logger.VerboseDebug($"Automap commanded to: {cmdData.State} "); + ClientAPI.TriggerChatMessage($"Automap commanded to: {cmdData.State} "); #endif - - } - - - 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(); - // 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(); - - 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(); - - 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 } -} \ No newline at end of file +}