Automap (client) [VS plugin mod]
Revision | 5156c68e472ccc1f71aa688c34f649f0a952ee45 (tree) |
---|---|
Time | 2020-02-29 14:25:41 |
Author | melchior <melchior@user...> |
Commiter | melchior |
Post-PR4 #2: Heightmap in PNG Metadata, JSON encoding for POI notes
partial PNG error mis-handling (untested)
@@ -70,6 +70,10 @@ | ||
70 | 70 | <HintPath>VS_libs\Pngcs.dll</HintPath> |
71 | 71 | </Reference> |
72 | 72 | <Reference Include="System.Xml" /> |
73 | + <Reference Include="Newtonsoft.Json"> | |
74 | + <HintPath>VS_libs\Newtonsoft.Json.dll</HintPath> | |
75 | + <Private>False</Private> | |
76 | + </Reference> | |
73 | 77 | </ItemGroup> |
74 | 78 | <ItemGroup> |
75 | 79 | <Compile Include="AutomapMod.cs" /> |
@@ -1,7 +1,8 @@ | ||
1 | 1 | using System; |
2 | 2 | using System.Collections.Generic; |
3 | 3 | using System.Collections.ObjectModel; |
4 | -using System.Linq; | |
4 | +using System.Collections.Specialized; | |
5 | + | |
5 | 6 | |
6 | 7 | using Vintagestory.API.MathTools; |
7 | 8 | using Vintagestory.API.Common; |
@@ -42,16 +43,23 @@ namespace Automap | ||
42 | 43 | public float ShrubDensity; |
43 | 44 | |
44 | 45 | [ProtoMember(10)] |
45 | - public ushort AirBlocks; | |
46 | + public uint AirBlocks; | |
46 | 47 | |
47 | 48 | [ProtoMember(11)] |
48 | - public ushort NonAirBlocks; | |
49 | + public uint NonAirBlocks; | |
50 | + | |
51 | + [ProtoMember(12)] | |
52 | + public byte ChunkSize; | |
53 | + | |
49 | 54 | |
50 | - //[ProtoMember(12,OverwriteList = true)] | |
51 | 55 | [ProtoIgnore] |
52 | - public ushort[,] HeightMap; | |
56 | + public ushort[,] HeightMap;//Needs to be 'flattened' for Protocol-Buffer serialization | |
57 | + | |
58 | + [ProtoMember(13)] | |
59 | + private ushort[ ] _flattened_HeightMap; | |
53 | 60 | |
54 | - public ColumnMeta(Vec2i loc, int chunkSize = 32) | |
61 | + | |
62 | + public ColumnMeta(Vec2i loc, byte chunkSize = 32) | |
55 | 63 | { |
56 | 64 | Location = loc; |
57 | 65 | ChunkAge = TimeSpan.Zero; |
@@ -64,7 +72,9 @@ namespace Automap | ||
64 | 72 | ShrubDensity = 0f; |
65 | 73 | AirBlocks = 0; |
66 | 74 | NonAirBlocks = 0; |
67 | - HeightMap = new ushort[chunkSize, chunkSize]; | |
75 | + ChunkSize = chunkSize; | |
76 | + HeightMap = new ushort[ChunkSize, ChunkSize]; | |
77 | + _flattened_HeightMap = null; | |
68 | 78 | } |
69 | 79 | |
70 | 80 | internal void UpdateFieldsFrom(ClimateCondition climate, IMapChunk mapChunk, TimeSpan chunkAge) |
@@ -77,8 +87,52 @@ namespace Automap | ||
77 | 87 | this.ShrubDensity = climate.ShrubDensity; |
78 | 88 | |
79 | 89 | this.YMax = mapChunk.YMax; |
90 | + } | |
91 | + | |
92 | + [ProtoBeforeSerialization] | |
93 | + private void PrepareData( ) | |
94 | + { | |
95 | + | |
96 | + if (HeightMap != null) { | |
97 | + _flattened_HeightMap = new ushort[ChunkSize * ChunkSize]; | |
98 | + int flatIndex = 0; | |
99 | + | |
100 | + for (byte col = 0; col < ChunkSize; col++) { | |
101 | + for (byte row = 0; row < ChunkSize; row++) { | |
102 | + _flattened_HeightMap[flatIndex] = HeightMap[col, row]; | |
103 | + flatIndex++; | |
104 | + } | |
105 | + } | |
106 | + | |
107 | + } | |
80 | 108 | |
81 | 109 | } |
110 | + | |
111 | + | |
112 | + [ProtoAfterDeserialization] | |
113 | + private void PostProcess( ) | |
114 | + { | |
115 | + if (this.HeightMap == null) this.HeightMap = new ushort[ChunkSize, ChunkSize]; | |
116 | + | |
117 | + if (_flattened_HeightMap != null) { | |
118 | + int col, row; | |
119 | + | |
120 | + BitVector32 bitMasker = new BitVector32(0); | |
121 | + var rowSection = BitVector32.CreateSection(ChunkSize); | |
122 | + var colSection = BitVector32.CreateSection(ChunkSize, rowSection); | |
123 | + | |
124 | + for (uint rowcol = 0; rowcol < (ChunkSize * ChunkSize); rowcol++) { | |
125 | + bitMasker = new BitVector32(data: ( int )rowcol); | |
126 | + row = bitMasker[rowSection]; | |
127 | + col = bitMasker[colSection]; | |
128 | + HeightMap[col, row] = _flattened_HeightMap[rowcol]; | |
129 | + } | |
130 | + | |
131 | + } | |
132 | + | |
133 | + } | |
134 | + | |
135 | + | |
82 | 136 | } |
83 | 137 | |
84 | 138 | public class ColumnsMetadata : KeyedCollection<Vec2i, ColumnMeta> |
@@ -9,7 +9,7 @@ using System.Threading; | ||
9 | 9 | |
10 | 10 | using Hjg.Pngcs; |
11 | 11 | using Hjg.Pngcs.Chunks; |
12 | - | |
12 | +using Newtonsoft.Json; | |
13 | 13 | using Vintagestory.API.Client; |
14 | 14 | using Vintagestory.API.Common; |
15 | 15 | using Vintagestory.API.Config; |
@@ -149,6 +149,7 @@ namespace Automap | ||
149 | 149 | { |
150 | 150 | uint ejectedItem = 0; |
151 | 151 | uint updatedChunks = 0; |
152 | + uint updatedPixels = 0; | |
152 | 153 | |
153 | 154 | //-- Should dodge enumerator changing underfoot....at a cost. |
154 | 155 | if (!columnCounter.IsEmpty) |
@@ -167,13 +168,19 @@ namespace Automap | ||
167 | 168 | continue; |
168 | 169 | } |
169 | 170 | |
170 | - ColumnMeta chunkMeta = CreateColumnMetadata(mostActiveCol, mapChunk); | |
171 | - PngWriter pngWriter = SetupPngImage(mostActiveCol.Key, chunkMeta); | |
171 | + ColumnMeta chunkMeta; | |
172 | + if (chunkTopMetadata.Contains(mostActiveCol.Key)) { | |
173 | + chunkMeta = chunkTopMetadata[mostActiveCol.Key]; | |
174 | + } | |
175 | + else { | |
176 | + chunkMeta = CreateColumnMetadata(mostActiveCol, mapChunk); | |
177 | + } | |
178 | + | |
172 | 179 | UpdateEntityMetadata(); |
173 | 180 | ProcessChunkBlocks(mostActiveCol.Key, mapChunk, chunkMeta); |
174 | 181 | |
175 | - | |
176 | - ChunkRenderer.GenerateChunkPngShard(mostActiveCol.Key, mapChunk, chunkMeta, pngWriter, out uint updatedPixels); | |
182 | + PngWriter pngWriter = SetupPngImage(mostActiveCol.Key, chunkMeta); | |
183 | + ChunkRenderer.GenerateChunkPngShard(mostActiveCol.Key, mapChunk, chunkMeta, pngWriter, out updatedPixels); | |
177 | 184 | |
178 | 185 | if (updatedPixels > 0) |
179 | 186 | { |
@@ -319,7 +326,7 @@ namespace Automap | ||
319 | 326 | jsonWriter.Write("airBlocks:'{0}',", shard.AirBlocks); |
320 | 327 | jsonWriter.Write("nonAirBlocks:'{0}',", shard.NonAirBlocks); |
321 | 328 | //TODO: Heightmap |
322 | - //TODO: Rock-ratio | |
329 | + //TODO: Rock-ratio, also requires a BlockID => Name lookup table....elsewhere | |
323 | 330 | jsonWriter.Write("}],"); |
324 | 331 | } |
325 | 332 | jsonWriter.Write("]);"); |
@@ -331,7 +338,7 @@ namespace Automap | ||
331 | 338 | jsonWriter.Write("['{0}_{1}',", poi.Location.X, poi.Location.Z); |
332 | 339 | jsonWriter.Write("{"); |
333 | 340 | jsonWriter.Write("prettyCoord:'{0}',", poi.Location.PrettyCoords(ClientAPI)); |
334 | - jsonWriter.Write("notes:'{0}',", poi.Notes.Replace("'", "\'").Replace("\n","\\n")); | |
341 | + jsonWriter.Write("notes:{0},", JsonConvert.ToString(poi.Notes, '\'', StringEscapeHandling.EscapeHtml)); | |
335 | 342 | jsonWriter.Write("time:new Date('{0}'),", poi.Timestamp.ToString("O")); |
336 | 343 | jsonWriter.Write("chunkPos:'{0}_{1}',", (poi.Location.X / chunkSize), (poi.Location.Z / chunkSize)); |
337 | 344 | jsonWriter.Write("}],"); |
@@ -342,7 +349,7 @@ namespace Automap | ||
342 | 349 | jsonWriter.Write("['{0}_{1}',", poi.Location.X, poi.Location.Z); |
343 | 350 | jsonWriter.Write("{"); |
344 | 351 | jsonWriter.Write("prettyCoord:'{0}',", poi.Location.PrettyCoords(ClientAPI)); |
345 | - jsonWriter.Write("notes:'{0}',", poi.Notes.Replace("'", "\'")); | |
352 | + jsonWriter.Write("notes:{0},", JsonConvert.ToString(poi.Notes, '\'', StringEscapeHandling.EscapeHtml)); | |
346 | 353 | jsonWriter.Write("time:new Date('{0}'),", poi.Timestamp.ToString("O")); |
347 | 354 | jsonWriter.Write("chunkPos:'{0}_{1}',", (poi.Location.X / chunkSize), (poi.Location.Z / chunkSize)); |
348 | 355 | jsonWriter.Write("}],"); |
@@ -357,7 +364,7 @@ namespace Automap | ||
357 | 364 | |
358 | 365 | private ColumnMeta CreateColumnMetadata(KeyValuePair<Vec2i, uint> mostActiveCol, IMapChunk mapChunk) |
359 | 366 | { |
360 | - ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), chunkSize); | |
367 | + ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), ( byte )chunkSize); | |
361 | 368 | BlockPos equivBP = new BlockPos(mostActiveCol.Key.X * chunkSize, |
362 | 369 | mapChunk.YMax, |
363 | 370 | mostActiveCol.Key.Y * chunkSize); |
@@ -383,37 +390,43 @@ namespace Automap | ||
383 | 390 | |
384 | 391 | if (files.Length > 0) |
385 | 392 | { |
386 | -#if DEBUG | |
393 | + #if DEBUG | |
387 | 394 | Logger.VerboseDebug("{0} Existing world chunk shards", files.Length); |
388 | -#endif | |
395 | + #endif | |
396 | + | |
397 | + foreach (var shardFile in files) { | |
389 | 398 | |
399 | + if (shardFile.Length < 1024) continue; | |
400 | + var result = chunkShardRegex.Match(shardFile.Name); | |
401 | + if (result.Success) { | |
402 | + int X_chunk_pos = int.Parse(result.Groups["X"].Value); | |
403 | + int Z_chunk_pos = int.Parse(result.Groups["Z"].Value); | |
390 | 404 | |
391 | - | |
392 | - foreach (var shardFile in files) | |
393 | - { | |
394 | - | |
395 | - if (shardFile.Length < 512) continue; | |
396 | - var result = chunkShardRegex.Match(shardFile.Name); | |
397 | - if (result.Success) | |
405 | + try | |
398 | 406 | { |
399 | - int X_chunk_pos = int.Parse(result.Groups["X"].Value); | |
400 | - int Z_chunk_pos = int.Parse(result.Groups["Z"].Value); | |
407 | + using (var fileStream = shardFile.OpenRead( )) { | |
401 | 408 | |
402 | - //Parse PNG chunks for METADATA in shard | |
403 | - using (var fileStream = shardFile.OpenRead( )) { | |
404 | - //TODO: Add corrupted PNG Exception handing HERE ! | |
405 | - PngReader pngRead = new PngReader(fileStream); | |
406 | - pngRead.ReadSkippingAllRows( ); | |
407 | - pngRead.End( ); | |
409 | + PngReader pngRead = new PngReader(fileStream); | |
410 | + pngRead.ReadSkippingAllRows( ); | |
411 | + pngRead.End( ); | |
412 | + //Parse PNG chunks for METADATA in shard | |
413 | + PngMetadataChunk metadataFromPng = pngRead.GetChunksList( ).GetById1(PngMetadataChunk.ID) as PngMetadataChunk; | |
408 | 414 | |
409 | - PngMetadataChunk metadataFromPng = pngRead.GetChunksList( ).GetById1(PngMetadataChunk.ID) as PngMetadataChunk; | |
415 | + chunkTopMetadata.Add(metadataFromPng.ChunkMetadata); | |
416 | + } | |
410 | 417 | |
411 | - chunkTopMetadata.Add(metadataFromPng.ChunkMetadata); | |
412 | - } | |
418 | + } | |
419 | + catch (PngjException someEx) | |
420 | + { | |
421 | + Logger.Error("PNG Corruption file '{0}' - Reason: {1}", shardFile.Name, someEx); | |
422 | + continue; | |
413 | 423 | } |
414 | 424 | } |
415 | 425 | |
426 | + } | |
416 | 427 | } |
428 | + | |
429 | + | |
417 | 430 | } |
418 | 431 | else |
419 | 432 | { |
@@ -444,12 +457,14 @@ namespace Automap | ||
444 | 457 | ChunkMetadata = metadata |
445 | 458 | }; |
446 | 459 | pngWriter.GetChunksList().Queue(pngChunkMeta); |
460 | + pngWriter.CompLevel = 9;// 9 is the maximum compression | |
461 | + pngWriter.CompressionStrategy = Hjg.Pngcs.Zlib.EDeflateCompressStrategy.Huffman; | |
447 | 462 | |
448 | 463 | return pngWriter; |
449 | 464 | } |
450 | 465 | |
451 | 466 | /// <summary> |
452 | - /// Does the heavy lifting of Scanning columns of chunks - creates Heightmap and Processes POIs, Entities, and stats... | |
467 | + /// Does the heavy lifting of Scanning columns of chunks - scans for BlockEntity, creates Heightmap and stats... | |
453 | 468 | /// </summary> |
454 | 469 | /// <param name="key">Chunk Coordinate</param> |
455 | 470 | /// <param name="mapChunk">Map chunk.</param> |
@@ -460,20 +475,21 @@ namespace Automap | ||
460 | 475 | int targetChunkY = mapChunk.YMax / chunkSize;//Surface ... |
461 | 476 | for (; targetChunkY > 0; targetChunkY--) |
462 | 477 | { |
463 | - if (!(ClientAPI.World.BlockAccessor.GetChunk(key.X, targetChunkY, key.Y) is WorldChunk chunkData) || chunkData.BlockEntities == null) | |
464 | - { | |
465 | -#if DEBUG | |
466 | - Logger.VerboseDebug("Chunk null or empty X{0} Y{1} Z{2}", key.X, targetChunkY, key.Y); | |
467 | -#endif | |
468 | - continue; | |
469 | - } | |
478 | + WorldChunk chunkData = ClientAPI.World.BlockAccessor.GetChunk(key.X, targetChunkY, key.Y) as WorldChunk; | |
479 | + | |
480 | + if (chunkData == null || chunkData.BlockEntities == null) { | |
481 | + #if DEBUG | |
482 | + Logger.VerboseDebug("Chunk null or empty X{0} Y{1} Z{2}", key.X, targetChunkY, key.Y); | |
483 | + #endif | |
484 | + continue; | |
485 | + } | |
470 | 486 | |
471 | 487 | /*************** Chunk Entities Scanning *********************/ |
472 | 488 | if (chunkData.BlockEntities != null && chunkData.BlockEntities.Length > 0) |
473 | 489 | { |
474 | -#if DEBUG | |
490 | + #if DEBUG | |
475 | 491 | Logger.VerboseDebug("Surface@ {0} = BlockEntities: {1}", key, chunkData.BlockEntities.Length); |
476 | -#endif | |
492 | + #endif | |
477 | 493 | |
478 | 494 | foreach (var blockEnt in chunkData.BlockEntities) |
479 | 495 | { |