internal const string _mapPath = @"Maps";
internal const string _chunkPath = @"Chunks";
+ internal const uint editThreshold = 1;
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(@"(?<X>[\d]+)_(?<Z>[\d]+)\.png", RegexOptions.Singleline);
- private ConcurrentDictionary<Vec2i, uint> columnCounter = new ConcurrentDictionary<Vec2i, uint>(3, 150);
+ private ConcurrentDictionary<Vec2i, ColumnCounter> columnCounters = new ConcurrentDictionary<Vec2i, ColumnCounter>(3, 150);
private ColumnsMetadata chunkTopMetadata;
private PointsOfInterest POIs = new PointsOfInterest();
private EntitiesOfInterest EOIs = new EntitiesOfInterest();
+ private Dictionary<int, ulong> blockTally;//TODO: TOTAL Road Tally
internal Dictionary<int, BlockDesignator> BlockID_Designators { get; private set; }
internal Dictionary<AssetLocation, EntityDesignator> Entity_Designators { get; private set; }
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.configuration.SeasonalColors);
+ this.ChunkRenderer = InstantiateChosenRenderer(config.RendererName);
//Listen on bus for commands
ClientAPI.Event.RegisterEventBusListener(CommandListener, 1.0, AutomapSystem.AutomapCommandEventKey);
private void ChunkAChanging(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason)
{
- 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);
- //}
+ 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)
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)
//TODO: REVISIT THIS CHUNK!
Logger.Warning("SKIP CHUNK: ({0}) - Map Chunk NULL!", mostActiveCol.Key);
nullMapCount++;
- columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
+ columnCounters.TryRemove(mostActiveCol.Key, out ejectedItem);
continue;
}
if (updatedPixels > 0)
{
#if DEBUG
- Logger.VerboseDebug("Wrote top-chunk shard: ({0}) - Edits#:{1}, Pixels#:{2}", mostActiveCol.Key, mostActiveCol.Value, updatedPixels);
+ 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);
+ columnCounters.TryRemove(mostActiveCol.Key, out ejectedItem);
#if DEBUG
Logger.VerboseDebug("Un-painted chunk shard: ({0}) ", mostActiveCol.Key);
#endif
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);
Logger.VerboseDebug("Thread '{0}' executing finally block.", Thread.CurrentThread.Name);
#endif
PersistPointsData();
+ Write_PlainMetadata( );
}
}
this.BlockID_Designators = new Dictionary<int, BlockDesignator>();
this.Entity_Designators = new Dictionary<AssetLocation, EntityDesignator>();
this.RockIdCodes = Helpers.ArbitrarytBlockIdHunter(ClientAPI, new AssetLocation(GlobalConstants.DefaultDomain, "rock-"), EnumBlockMaterial.Stone);
- this.AiryIdCodes = Helpers.ArbitrarytBlockIdHunter(ClientAPI, new AssetLocation(GlobalConstants.DefaultDomain, "tallgrass-"), EnumBlockMaterial.Plant);
- //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();
}
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");
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(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();
}
- private ColumnMeta CreateColumnMetadata(KeyValuePair<Vec2i, uint> 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("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.Flush( );
+ }
+ }
+ }
+
+
+ private ColumnMeta CreateColumnMetadata(KeyValuePair<Vec2i, ColumnCounter> mostActiveCol, IMapChunk mapChunk)
{
ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), ClientAPI, (byte) chunkSize, (ClientAPI.World.BlockAccessor.MapSizeY / chunkSize));
BlockPos equivBP = new BlockPos(mostActiveCol.Key.X * chunkSize,
AddNote(cmdData.Notation);
break;
}
-#if DEBUG
+ #if DEBUG
ClientAPI.TriggerChatMessage($"Automap commanded to: {cmdData.State} ");
-#endif
+ #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...");
+ }
+
+ return null;
+ }
}
}