X-Git-Url: http://git.osdn.net/view?p=automap%2Fautomap.git;a=blobdiff_plain;f=Automap%2FSubsystems%2FAutomapSystem.cs;h=2e35db3673452bd36293eaa304b598c2e86466cd;hp=2997ec6a19f3e386fd4ec09dab68137458191b09;hb=71f44a94cf2ad64387400c832e52fb8ba0d838a7;hpb=6f21853a9ca96dc7cd115f471e296e6f8d3ab655 diff --git a/Automap/Subsystems/AutomapSystem.cs b/Automap/Subsystems/AutomapSystem.cs index 2997ec6..2e35db3 100644 --- a/Automap/Subsystems/AutomapSystem.cs +++ b/Automap/Subsystems/AutomapSystem.cs @@ -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; @@ -43,6 +44,7 @@ namespace Automap private static Regex chunkShardRegex = new Regex(@"(?[\d]+)_(?[\d]+)\.png", RegexOptions.Singleline); private ConcurrentDictionary columnCounters = new ConcurrentDictionary(3, 150); + private Queue revisitChunkList = new Queue(); private ColumnsMetadata chunkTopMetadata; internal PointsOfInterest POIs = new PointsOfInterest(); internal EntitiesOfInterest EOIs = new EntitiesOfInterest(); @@ -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.Notification("Autostart is Enabled."); - } - } @@ -122,6 +116,10 @@ namespace Automap }; ClientAPI.Event.RegisterGameTickListener(ThreadDecider, 6000); + + if (configuration.Autostart) { + CurrentState = CommandType.Run; + } } private void ChunkAChanging(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason) @@ -132,8 +130,7 @@ namespace Automap columnCounters.AddOrUpdate(topPosition, new ColumnCounter(chunkSize, newOrEdit, chunkCoord), (chkPos, chkChng) => chkChng.Update(chunkCoord, chunkSize, newOrEdit) - ); - + ); } /// @@ -174,7 +171,7 @@ namespace Automap } 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); + 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; @@ -194,24 +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; } @@ -230,11 +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); - mostActiveCol.Value.SetCutoff(chunkMeta.YMax / chunkSize); + 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) { @@ -243,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); } } } @@ -275,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( ); } @@ -309,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; } } @@ -346,14 +393,24 @@ 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 ||airyBlock.RainPermeable == true - 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() @@ -501,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; + } + /// /// Reload chunk bounds from chunk shards /// @@ -612,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; @@ -626,22 +696,31 @@ namespace Automap #if DEBUG Logger.VerboseDebug("WORLD chunk: Compressed: X{0} Y{1} Z{2}", key.X, targetChunkY, key.Y); #endif - worldChunk.Unpack( );//RESEARCH: Thread Unsafe? + //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.Key != null && 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); } } } @@ -652,7 +731,7 @@ 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 @@ -671,7 +750,7 @@ 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 @@ -679,7 +758,7 @@ namespace Automap goto loop_bustout; } - int aBlockId = worldChunk.Blocks[indicie]; + int aBlockId = worldChunk.MaybeBlocks[indicie]; if (aBlockId == 0 || AiryIdCodes.ContainsKey(aBlockId)) {//Airy blocks,,, chunkMeta.AirBlocks++; @@ -714,26 +793,30 @@ namespace Automap private void UpdateEntityMetadata() { - #if DEBUG - Logger.Debug("Presently {0} Entities", ClientAPI.World.LoadedEntities.Count); - #endif - //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) @@ -799,7 +882,7 @@ namespace Automap } return null; - } + } } }