• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Keine Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Automap (client) [VS plugin mod]


Commit MetaInfo

Revision3a26629e3ee0961a34ddcac63e636581a371a7b1 (tree)
Zeit2020-02-25 09:55:26
AutorThe Grand Dog <alex.h@me.c...>
CommiterThe Grand Dog

Log Message

Merge branch 'Split_renderers' into vgd

Ändern Zusammenfassung

Diff

--- a/Automap/Automap.csproj
+++ b/Automap/Automap.csproj
@@ -69,6 +69,7 @@
6969 <Reference Include="Pngcs">
7070 <HintPath>VS_libs\Pngcs.dll</HintPath>
7171 </Reference>
72+ <Reference Include="System.Xml" />
7273 </ItemGroup>
7374 <ItemGroup>
7475 <Compile Include="AutomapMod.cs" />
@@ -86,6 +87,9 @@
8687 <Compile Include="Renderers\AlternateRenderer.cs" />
8788 <Compile Include="Data\EntitiesOfInterest.cs" />
8889 <Compile Include="Data\EntityDesignator.cs" />
90+ <Compile Include="Data\StatusData.cs" />
91+ <Compile Include="Data\CommandData.cs" />
92+ <Compile Include="Data\RunState.cs" />
8993 </ItemGroup>
9094 <ItemGroup>
9195 <Folder Include="VS_libs\" />
--- a/Automap/AutomapMod.cs
+++ b/Automap/AutomapMod.cs
@@ -19,41 +19,39 @@ namespace Automap
1919 return forSide.IsClient();
2020 }
2121
22- public override void StartClientSide(ICoreClientAPI api)
23- {
24- this.API = api;
22+ public override void StartClientSide(ICoreClientAPI api)
23+ {
24+ this.API = api;
2525
26- if (api.Side == EnumAppSide.Client)
27- {
28- this.ClientAPI = api as ICoreClientAPI;
29- this.Logger = Mod.Logger;
26+ if (api.Side == EnumAppSide.Client)
27+ {
28+ this.ClientAPI = api as ICoreClientAPI;
29+ this.Logger = Mod.Logger;
3030
3131
32- ClientAPI.Logger.VerboseDebug("Automap Present!");
33- _localAutomap = new AutomapSystem(this.ClientAPI, this.Logger);
34- _automapDialog = new AutomapGUIDialog(ClientAPI, _localAutomap);
32+ ClientAPI.Logger.VerboseDebug("Automap Present!");
33+ _localAutomap = new AutomapSystem(this.ClientAPI, this.Logger);
34+ _automapDialog = new AutomapGUIDialog(ClientAPI, _localAutomap);
3535
36- ClientAPI.Input.RegisterHotKey(AutomapGUIDialog._automapControlPanelKey, "Automap control panel", GlKeys.A, HotkeyType.GUIOrOtherControls);
37- ClientAPI.Input.SetHotKeyHandler(AutomapGUIDialog._automapControlPanelKey, ToggleAM_Dialog);
38- }
36+ ClientAPI.Input.RegisterHotKey(AutomapGUIDialog._automapControlPanelKey, "Automap control panel", GlKeys.M, HotkeyType.GUIOrOtherControls, shiftPressed: true);
37+ ClientAPI.Input.SetHotKeyHandler(AutomapGUIDialog._automapControlPanelKey, ToggleAM_Dialog);
38+ }
3939
4040 base.StartClientSide(api);
4141 }
4242
43- public override double ExecuteOrder()
44- {
45- return 0.2;
46- }
47-
48- private bool ToggleAM_Dialog(KeyCombination comb)
49- {
50- if (_automapDialog.IsOpened()) _automapDialog.TryClose();
51- else _automapDialog.TryOpen();
52-
53- return true;
54- }
43+ public override double ExecuteOrder()
44+ {
45+ return 0.2;
46+ }
5547
48+ private bool ToggleAM_Dialog(KeyCombination comb)
49+ {
50+ if (_automapDialog.IsOpened()) _automapDialog.TryClose();
51+ else _automapDialog.TryOpen();
5652
57- }
53+ return true;
54+ }
55+ }
5856 }
5957
--- a/Automap/Data/BlockDesignator.cs
+++ b/Automap/Data/BlockDesignator.cs
@@ -9,45 +9,45 @@ using Vintagestory.API.MathTools;
99
1010 namespace Automap
1111 {
12- public delegate void BlockDesignatonAction(ICoreClientAPI clientAPI, PointsOfInterest poi, BlockPos posn, Block block);
13-
14- /// <summary>
15- /// Point of Interest Rule Designator
16- /// </summary>
17- public class BlockDesignator
18- {
19- public Color OverwriteColor;
20- public BlockDesignatonAction SpecialAction;
21- public AssetLocation Pattern;
22- public EnumBlockMaterial? Material;
23- public bool Enabled { get; set; }
24-
25- private BlockDesignator()
26- {
27- throw new NotSupportedException();
28- }
29-
30- public BlockDesignator(AssetLocation pattern, Color overwriteColor, EnumBlockMaterial? material)
31- {
32- this.Pattern = pattern;
33- this.OverwriteColor = overwriteColor;
34- this.Material = material;
35- this.Enabled = true;
36- }
37-
38- public BlockDesignator(AssetLocation pattern, Color overwriteColor, EnumBlockMaterial? material, BlockDesignatonAction specialAct)
39- {
40- this.Pattern = pattern;
41- this.OverwriteColor = overwriteColor;
42- this.Material = material;
43- this.SpecialAction = specialAct;
44- this.Enabled = true;
45- }
46-
47- public override string ToString()
48- {
49- return Pattern.ToShortString() + "|" + OverwriteColor.Name + "|" + Material ?? "";
50- }
51- }
12+ public delegate void BlockDesignatonAction(ICoreClientAPI clientAPI, PointsOfInterest poi, BlockPos posn, Block block);
13+
14+ /// <summary>
15+ /// Point of Interest Rule Designator
16+ /// </summary>
17+ public class BlockDesignator
18+ {
19+ public Color OverwriteColor;
20+ public BlockDesignatonAction SpecialAction;
21+ public AssetLocation Pattern;
22+ public EnumBlockMaterial? Material;
23+ public bool Enabled { get; set; }
24+
25+ private BlockDesignator()
26+ {
27+ throw new NotSupportedException();
28+ }
29+
30+ public BlockDesignator(AssetLocation pattern, Color overwriteColor, EnumBlockMaterial? material)
31+ {
32+ this.Pattern = pattern;
33+ this.OverwriteColor = overwriteColor;
34+ this.Material = material;
35+ this.Enabled = true;
36+ }
37+
38+ public BlockDesignator(AssetLocation pattern, Color overwriteColor, EnumBlockMaterial? material, BlockDesignatonAction specialAct)
39+ {
40+ this.Pattern = pattern;
41+ this.OverwriteColor = overwriteColor;
42+ this.Material = material;
43+ this.SpecialAction = specialAct;
44+ this.Enabled = true;
45+ }
46+
47+ public override string ToString()
48+ {
49+ return Pattern.ToShortString() + "|" + OverwriteColor.Name + "|" + Material ?? "";
50+ }
51+ }
5252 }
5353
--- a/Automap/Data/ColumnMeta.cs
+++ b/Automap/Data/ColumnMeta.cs
@@ -11,155 +11,155 @@ using ProtoBuf;
1111
1212 namespace Automap
1313 {
14- [ProtoContract]
15- public struct ColumnMeta
16- {
17- [ProtoMember(1)]
18- public Vec2i Location;
19-
20- [ProtoMember(2)]
21- public TimeSpan ChunkAge;//In game, calendar?
22-
23- [ProtoMember(3)]
24- public float Temperature;// Temperature
25-
26- [ProtoMember(4)]
27- public ushort YMax;// Y feature height
28-
29- [ProtoMember(5)]
30- public Dictionary<int, uint> RockRatio;//(surface) Geographic region (rock) Ratio. [BlockID * count]
31-
32- [ProtoMember(6)]
33- public float Fertility;
34-
35- [ProtoMember(7)]
36- public float ForestDensity;
37-
38- [ProtoMember(8)]
39- public float Rainfall;
40-
41- [ProtoMember(9)]
42- public float ShrubDensity;
43-
44- [ProtoMember(10)]
45- public ushort AirBlocks;
46-
47- [ProtoMember(11)]
48- public ushort NonAirBlocks;
49-
50- //[ProtoMember(12,OverwriteList = true)]
51- [ProtoIgnore]
52- public ushort[,] HeightMap;
53-
54- public ColumnMeta(Vec2i loc, int chunkSize = 32)
55- {
56- Location = loc;
57- ChunkAge = TimeSpan.Zero;
58- Temperature = 0f;
59- YMax = 0;
60- RockRatio = new Dictionary<int, uint>(10);
61- Fertility = 0f;
62- ForestDensity = 0f;
63- Rainfall = 0f;
64- ShrubDensity = 0f;
65- AirBlocks = 0;
66- NonAirBlocks = 0;
67- HeightMap = new ushort[chunkSize, chunkSize];
68- }
69-
70- internal void UpdateFieldsFrom(ClimateCondition climate, IMapChunk mapChunk, TimeSpan chunkAge)
71- {
72- this.ChunkAge = chunkAge;
73- this.Temperature = climate.Temperature;
74- this.Fertility = climate.Fertility;
75- this.ForestDensity = climate.ForestDensity;
76- this.Rainfall = climate.Rainfall;
77- this.ShrubDensity = climate.ShrubDensity;
78-
79- this.YMax = mapChunk.YMax;
80-
81- }
82- }
83-
84- public class ColumnsMetadata : KeyedCollection<Vec2i, ColumnMeta>
85- {
86- private ColumnsMetadata()
87- {
88- throw new NotSupportedException();
89- }
90-
91- public ColumnsMetadata(Vec2i startChunkColumn)
92- {
93- North_mostChunk = startChunkColumn.Y;
94- South_mostChunk = startChunkColumn.Y;
95- East_mostChunk = startChunkColumn.X;
96- West_mostChunk = startChunkColumn.X;
97- }
98-
99- public int North_mostChunk
100- {
101- get; private set;
102- }
103-
104- public int South_mostChunk
105- {
106- get; private set;
107- }
108-
109- public int East_mostChunk
110- {
111- get; private set;
112- }
113-
114- public int West_mostChunk
115- {
116- get; private set;
117- }
118-
119- protected override Vec2i GetKeyForItem(ColumnMeta item)
120- {
121- return item.Location;
122- }
123-
124- internal void Update(ColumnMeta metaData)
125- {
126- if (this.Contains(metaData.Location))
127- {
128- this.Remove(metaData.Location);
129- this.Add(metaData);
130- }
131- else
132- {
133- this.Add(metaData);
134- }
135-
136- }
137-
138- public new void Add(ColumnMeta newItem)
139- {
140- if (North_mostChunk > newItem.Location.Y)
141- {
142- North_mostChunk = newItem.Location.Y;
143- }
144-
145- if (South_mostChunk < newItem.Location.Y)
146- {
147- South_mostChunk = newItem.Location.Y;
148- }
149-
150- if (East_mostChunk < newItem.Location.X)
151- {
152- East_mostChunk = newItem.Location.X;
153- }
154-
155- if (West_mostChunk > newItem.Location.X)
156- {
157- West_mostChunk = newItem.Location.X;
158- }
159-
160- base.Add(newItem);
161- }
162-
163- }
14+ [ProtoContract]
15+ public struct ColumnMeta
16+ {
17+ [ProtoMember(1)]
18+ public Vec2i Location;
19+
20+ [ProtoMember(2)]
21+ public TimeSpan ChunkAge;//OLDEST CHUNK. from chunk last edit
22+
23+ [ProtoMember(3)]
24+ public float Temperature;// Temperature - surface
25+
26+ [ProtoMember(4)]
27+ public ushort YMax;// Y feature height
28+
29+ [ProtoMember(5)]
30+ public Dictionary<int, uint> RockRatio;//[Column] Geographic region (rock) Ratio. [BlockID * count]
31+
32+ [ProtoMember(6)]
33+ public float Fertility;
34+
35+ [ProtoMember(7)]
36+ public float ForestDensity;
37+
38+ [ProtoMember(8)]
39+ public float Rainfall;
40+
41+ [ProtoMember(9)]
42+ public float ShrubDensity;
43+
44+ [ProtoMember(10)]
45+ public ushort AirBlocks;
46+
47+ [ProtoMember(11)]
48+ public ushort NonAirBlocks;
49+
50+ //[ProtoMember(12,OverwriteList = true)]
51+ [ProtoIgnore]
52+ public ushort[,] HeightMap;
53+
54+ public ColumnMeta(Vec2i loc, int chunkSize = 32)
55+ {
56+ Location = loc;
57+ ChunkAge = TimeSpan.Zero;
58+ Temperature = 0f;
59+ YMax = 0;
60+ RockRatio = new Dictionary<int, uint>(10);
61+ Fertility = 0f;
62+ ForestDensity = 0f;
63+ Rainfall = 0f;
64+ ShrubDensity = 0f;
65+ AirBlocks = 0;
66+ NonAirBlocks = 0;
67+ HeightMap = new ushort[chunkSize, chunkSize];
68+ }
69+
70+ internal void UpdateFieldsFrom(ClimateCondition climate, IMapChunk mapChunk, TimeSpan chunkAge)
71+ {
72+ this.ChunkAge = chunkAge;
73+ this.Temperature = climate.Temperature;
74+ this.Fertility = climate.Fertility;
75+ this.ForestDensity = climate.ForestDensity;
76+ this.Rainfall = climate.Rainfall;
77+ this.ShrubDensity = climate.ShrubDensity;
78+
79+ this.YMax = mapChunk.YMax;
80+
81+ }
82+ }
83+
84+ public class ColumnsMetadata : KeyedCollection<Vec2i, ColumnMeta>
85+ {
86+ private ColumnsMetadata()
87+ {
88+ throw new NotSupportedException();
89+ }
90+
91+ public ColumnsMetadata(Vec2i startChunkColumn)
92+ {
93+ North_mostChunk = startChunkColumn.Y;
94+ South_mostChunk = startChunkColumn.Y;
95+ East_mostChunk = startChunkColumn.X;
96+ West_mostChunk = startChunkColumn.X;
97+ }
98+
99+ public int North_mostChunk
100+ {
101+ get; private set;
102+ }
103+
104+ public int South_mostChunk
105+ {
106+ get; private set;
107+ }
108+
109+ public int East_mostChunk
110+ {
111+ get; private set;
112+ }
113+
114+ public int West_mostChunk
115+ {
116+ get; private set;
117+ }
118+
119+ protected override Vec2i GetKeyForItem(ColumnMeta item)
120+ {
121+ return item.Location;
122+ }
123+
124+ internal void Update(ColumnMeta metaData)
125+ {
126+ if (this.Contains(metaData.Location))
127+ {
128+ this.Remove(metaData.Location);
129+ this.Add(metaData);
130+ }
131+ else
132+ {
133+ this.Add(metaData);
134+ }
135+
136+ }
137+
138+ public new void Add(ColumnMeta newItem)
139+ {
140+ if (North_mostChunk > newItem.Location.Y)
141+ {
142+ North_mostChunk = newItem.Location.Y;
143+ }
144+
145+ if (South_mostChunk < newItem.Location.Y)
146+ {
147+ South_mostChunk = newItem.Location.Y;
148+ }
149+
150+ if (East_mostChunk < newItem.Location.X)
151+ {
152+ East_mostChunk = newItem.Location.X;
153+ }
154+
155+ if (West_mostChunk > newItem.Location.X)
156+ {
157+ West_mostChunk = newItem.Location.X;
158+ }
159+
160+ base.Add(newItem);
161+ }
162+
163+ }
164164 }
165165
--- /dev/null
+++ b/Automap/Data/CommandData.cs
@@ -0,0 +1,116 @@
1+using System;
2+using System.Collections;
3+using System.Collections.Generic;
4+using System.Drawing;
5+using System.IO;
6+
7+using ProtoBuf;
8+
9+using Vintagestory.API.Common;
10+using Vintagestory.API.Datastructures;
11+
12+namespace Automap
13+{
14+ [ProtoContract(SkipConstructor = true)]
15+ //[ProtoInclude(5,typeof(DelegateState))]
16+ public class CommandData : IAttribute
17+ {
18+ [ProtoMember(1)]
19+ public RunState State { get; set; }//Run , Stop, SingleSnapshot -> Stop
20+
21+ [ProtoMember(2)]
22+ private List<DelegateState> DelegatesFlags;
23+
24+ [ProtoMember(3)]
25+ public string Notation { get; set; }
26+
27+ //POI Delegate list {enabled/Disable}, color?
28+ //Other params...? Tick rate?
29+ //Choose : Renderer(s)
30+
31+
32+ public CommandData(RunState assumeState, bool[] theseDelegates)
33+ {
34+ State = assumeState;
35+
36+ DelegatesFlags = new List<DelegateState>(theseDelegates.Length);
37+ foreach (var df in theseDelegates)
38+ {
39+ DelegatesFlags.Add(new DelegateState()
40+ {
41+ Enabled = df,
42+ AlternateColor = null,
43+ });
44+
45+ }
46+
47+ }
48+
49+ public CommandData(RunState assumeState)
50+ {
51+ State = assumeState;//Never RUN.
52+
53+ DelegatesFlags = new List<DelegateState>();
54+ }
55+
56+
57+ public void FromBytes(BinaryReader stream)
58+ {
59+ var temp = ProtoBuf.Serializer.Deserialize<CommandData>(stream.BaseStream);
60+ this.State = temp.State;
61+ this.DelegatesFlags = temp.DelegatesFlags;
62+
63+ }
64+
65+ public void ToBytes(BinaryWriter stream)
66+ {
67+ ProtoBuf.Serializer.Serialize<CommandData>(stream.BaseStream, this);
68+
69+
70+ }
71+
72+
73+
74+ public int GetAttributeId()
75+ {
76+ return 12346;
77+ }
78+
79+ public object GetValue()
80+ {
81+ return this;
82+ }
83+
84+
85+ public string ToJsonToken()
86+ {
87+ return $"New-State:{State}, Delegates# {DelegatesFlags.Count} ";
88+ }
89+
90+ public bool Equals(IWorldAccessor worldForResolve, IAttribute attr)
91+ {
92+ var other = attr.GetValue() as CommandData;
93+
94+ if (this.State == other.State)
95+ {
96+ return true;
97+ }
98+
99+ return false;
100+ }
101+ }
102+
103+ [ProtoContract]
104+ internal struct DelegateState
105+ {
106+ [ProtoMember(1)]
107+ public bool Enabled;
108+
109+ [ProtoMember(2)]
110+ public Color? AlternateColor;
111+
112+
113+
114+ }
115+}
116+
--- a/Automap/Data/EntitiesOfInterest.cs
+++ b/Automap/Data/EntitiesOfInterest.cs
@@ -8,44 +8,43 @@ using Vintagestory.API.Common.Entities;
88
99 namespace Automap
1010 {
11- /// <summary>
12- /// Entities of interest.
13- /// </summary>
14- /// <remarks>Tracked by ID - these never leave.</remarks>
15- public class EntitiesOfInterest
16- {
17- private Dictionary<long, PointOfInterest> entitySet = new Dictionary<long, PointOfInterest>(50);
18-
19-
20- internal void Upsert(Entity something, string message = @"")
21- {
22- if (entitySet.ContainsKey(something.EntityId))
23- {
24- var movingPOI = entitySet[something.EntityId];
25- movingPOI.Location = something.Pos.AsBlockPos.Copy();
26- movingPOI.Timestamp = DateTimeOffset.UtcNow;
27- }
28- else
29- {
30- PointOfInterest newPOI = new PointOfInterest();
31- newPOI.EntityId = something.EntityId;
32- newPOI.Location = something.Pos.AsBlockPos.Copy();
33- newPOI.Timestamp = DateTimeOffset.UtcNow;
34- newPOI.Notes = message;
35- entitySet.Add(something.EntityId, newPOI);
36- }
37-
38- }
39-
40-
41- public List<PointOfInterest> PointsList
42- {
43- get
44- {
45- return entitySet.Values.ToList();
46- }
47- }
48-
49- }
11+ /// <summary>
12+ /// Entities of interest.
13+ /// </summary>
14+ /// <remarks>Tracked by ID - these never leave.</remarks>
15+ public class EntitiesOfInterest
16+ {
17+ private Dictionary<long, PointOfInterest> entitySet = new Dictionary<long, PointOfInterest>(50);
18+
19+
20+ internal void Upsert(Entity something, string message = @"")
21+ {
22+ if (entitySet.ContainsKey(something.EntityId))
23+ {
24+ var movingPOI = entitySet[something.EntityId];
25+ movingPOI.Location = something.Pos.AsBlockPos.Copy();
26+ movingPOI.Timestamp = DateTimeOffset.UtcNow;
27+ }
28+ else
29+ {
30+ PointOfInterest newPOI = new PointOfInterest();
31+ newPOI.EntityId = something.EntityId;
32+ newPOI.Location = something.Pos.AsBlockPos.Copy();
33+ newPOI.Timestamp = DateTimeOffset.UtcNow;
34+ newPOI.Notes = message;
35+ entitySet.Add(something.EntityId, newPOI);
36+ }
37+
38+ }
39+
40+
41+ public List<PointOfInterest> PointsList
42+ {
43+ get {
44+ return entitySet.Values.ToList();
45+ }
46+ }
47+
48+ }
5049 }
5150
--- a/Automap/Data/EntityDesignator.cs
+++ b/Automap/Data/EntityDesignator.cs
@@ -10,45 +10,45 @@ using Vintagestory.API.MathTools;
1010
1111 namespace Automap
1212 {
13- public delegate void EntityDesignatonAction(ICoreClientAPI clientAPI, EntitiesOfInterest poi, BlockPos posn, Entity entity);
14-
15- /// <summary>
16- /// Point of Interest Rule Designator
17- /// </summary>
18- public class EntityDesignator
19- {
20- public Color OverwriteColor;
21- public EntityDesignatonAction SpecialAction;
22- public AssetLocation Pattern;
23- public EnumEntityState? StateCheck;//Needed?
24- public bool Enabled { get; set; }
25-
26- private EntityDesignator()
27- {
28- throw new NotSupportedException();
29- }
30-
31- public EntityDesignator(AssetLocation pattern, Color overwriteColor, EnumEntityState? state)
32- {
33- this.Pattern = pattern;
34- this.OverwriteColor = overwriteColor;
35- this.StateCheck = state;
36- this.Enabled = true;
37- }
38-
39- public EntityDesignator(AssetLocation pattern, Color overwriteColor, EnumEntityState? state, EntityDesignatonAction specialAct)
40- {
41- this.Pattern = pattern;
42- this.OverwriteColor = overwriteColor;
43- this.StateCheck = state;
44- this.SpecialAction = specialAct;
45- this.Enabled = true;
46- }
47-
48- public override string ToString()
49- {
50- return Pattern.ToShortString() + "|" + OverwriteColor.Name + "|" + StateCheck ?? "";
51- }
52- }
13+ public delegate void EntityDesignatonAction(ICoreClientAPI clientAPI, EntitiesOfInterest poi, BlockPos posn, Entity entity);
14+
15+ /// <summary>
16+ /// Point of Interest Rule Designator
17+ /// </summary>
18+ public class EntityDesignator
19+ {
20+ public Color OverwriteColor;
21+ public EntityDesignatonAction SpecialAction;
22+ public AssetLocation Pattern;
23+ public EnumEntityState? StateCheck;//Needed?
24+ public bool Enabled { get; set; }
25+
26+ private EntityDesignator()
27+ {
28+ throw new NotSupportedException();
29+ }
30+
31+ public EntityDesignator(AssetLocation pattern, Color overwriteColor, EnumEntityState? state)
32+ {
33+ this.Pattern = pattern;
34+ this.OverwriteColor = overwriteColor;
35+ this.StateCheck = state;
36+ this.Enabled = true;
37+ }
38+
39+ public EntityDesignator(AssetLocation pattern, Color overwriteColor, EnumEntityState? state, EntityDesignatonAction specialAct)
40+ {
41+ this.Pattern = pattern;
42+ this.OverwriteColor = overwriteColor;
43+ this.StateCheck = state;
44+ this.SpecialAction = specialAct;
45+ this.Enabled = true;
46+ }
47+
48+ public override string ToString()
49+ {
50+ return Pattern.ToShortString() + "|" + OverwriteColor.Name + "|" + StateCheck ?? "";
51+ }
52+ }
5353 }
5454
--- a/Automap/Data/PngMetadataChunk.cs
+++ b/Automap/Data/PngMetadataChunk.cs
@@ -7,49 +7,49 @@ using Hjg.Pngcs.Chunks;
77
88 namespace Automap
99 {
10- /// <summary>
11- /// Png metadata chunk.
12- /// </summary>
13- /// <remarks>There can be only one. (per PNG file)</remarks>
14- public class PngMetadataChunk : PngChunkSingle
15- {
16- // ID must follow the PNG conventions: four ascii letters,
17- public readonly static string ID = "cHUK";
10+ /// <summary>
11+ /// Png metadata chunk.
12+ /// </summary>
13+ /// <remarks>There can be only one. (per PNG file)</remarks>
14+ public class PngMetadataChunk : PngChunkSingle
15+ {
16+ // ID must follow the PNG conventions: four ascii letters,
17+ public readonly static string ID = "cHUK";
1818
19- public ColumnMeta ChunkMetadata { get; set; }
19+ public ColumnMeta ChunkMetadata { get; set; }
2020
2121
22- public PngMetadataChunk(ImageInfo info) : base(ID, info)
23- {
22+ public PngMetadataChunk(ImageInfo info) : base(ID, info)
23+ {
2424
25- }
25+ }
2626
27- public override ChunkOrderingConstraint GetOrderingConstraint()
28- {
29- return ChunkOrderingConstraint.NONE;
30- }
27+ public override ChunkOrderingConstraint GetOrderingConstraint()
28+ {
29+ return ChunkOrderingConstraint.NONE;
30+ }
3131
32- public override ChunkRaw CreateRawChunk()
33- {
34- var datas = SerializerUtil.Serialize<ColumnMeta>(ChunkMetadata);
32+ public override ChunkRaw CreateRawChunk()
33+ {
34+ var datas = SerializerUtil.Serialize<ColumnMeta>(ChunkMetadata);
3535
36- ChunkRaw rawChunk = createEmptyChunk(datas.Length, true);
37- rawChunk.Data = datas;
36+ ChunkRaw rawChunk = createEmptyChunk(datas.Length, true);
37+ rawChunk.Data = datas;
3838
39- return rawChunk;
40- }
39+ return rawChunk;
40+ }
4141
42- public override void ParseFromRaw(ChunkRaw rawChunk)
43- {
44- this.ChunkMetadata = SerializerUtil.Deserialize<ColumnMeta>(rawChunk.Data);
45- }
42+ public override void ParseFromRaw(ChunkRaw rawChunk)
43+ {
44+ this.ChunkMetadata = SerializerUtil.Deserialize<ColumnMeta>(rawChunk.Data);
45+ }
4646
47- public override void CloneDataFromRead(PngChunk other)
48- {
49- PngMetadataChunk clone = (PngMetadataChunk)other;
50- this.ChunkMetadata = clone.ChunkMetadata;
51- }
47+ public override void CloneDataFromRead(PngChunk other)
48+ {
49+ PngMetadataChunk clone = (PngMetadataChunk) other;
50+ this.ChunkMetadata = clone.ChunkMetadata;
51+ }
5252
53- }
53+ }
5454 }
5555
--- a/Automap/Data/PointOfInterest.cs
+++ b/Automap/Data/PointOfInterest.cs
@@ -6,35 +6,35 @@ using Vintagestory.API.MathTools;
66
77 namespace Automap
88 {
9- /// <summary>
10- /// Actual Physical Point in space - that is interesting.
11- /// </summary>
12- public struct PointOfInterest
13- {
14- public string Notes;
15- public BlockPos Location;
16- public DateTimeOffset Timestamp;
17- public long? EntityId;
18- }
9+ /// <summary>
10+ /// Actual Physical Point in space - that is interesting.
11+ /// </summary>
12+ public struct PointOfInterest
13+ {
14+ public string Notes;
15+ public BlockPos Location;
16+ public DateTimeOffset Timestamp;
17+ public long? EntityId;
18+ }
1919
20- public class PointsOfInterest : KeyedCollection<BlockPos, PointOfInterest>
21- {
22- protected override BlockPos GetKeyForItem(PointOfInterest item)
23- {
24- return item.Location;
25- }
20+ public class PointsOfInterest : KeyedCollection<BlockPos, PointOfInterest>
21+ {
22+ protected override BlockPos GetKeyForItem(PointOfInterest item)
23+ {
24+ return item.Location;
25+ }
2626
27- internal void AddReplace(PointOfInterest poi)
28- {
29- if (this.Contains(poi.Location))
30- {
31- this.Remove(poi.Location);
32- this.Add(poi);
33- }
34- else
35- {
36- this.Add(poi);
37- }
27+ internal void AddReplace(PointOfInterest poi)
28+ {
29+ if (this.Contains(poi.Location))
30+ {
31+ this.Remove(poi.Location);
32+ this.Add(poi);
33+ }
34+ else
35+ {
36+ this.Add(poi);
37+ }
3838
3939 }
4040 }
--- /dev/null
+++ b/Automap/Data/RunState.cs
@@ -0,0 +1,15 @@
1+using System;
2+namespace Automap
3+{
4+ /// <summary>
5+ /// Command Type
6+ /// </summary>
7+ public enum RunState : byte
8+ {
9+ Stop = 0x00,
10+ Run = 0x01,
11+ Snapshot = 0x02,
12+ Notation = 0x03,
13+ }
14+}
15+
--- /dev/null
+++ b/Automap/Data/StatusData.cs
@@ -0,0 +1,60 @@
1+using System;
2+using System.IO;
3+using Vintagestory.API.Common;
4+using Vintagestory.API.Datastructures;
5+
6+namespace Automap {
7+ public class StatusData : IAttribute {
8+ public uint TotalUpdates { get; set; }
9+ public uint VoidChunks { get; set; }
10+ public uint Delta { get; set; }
11+ public uint Max_N, Max_E, Max_S, Max_W;
12+ public RunState CurrentState { get; set; }
13+
14+ public StatusData(uint totalUpdates, uint voidChunks, uint delta, RunState currently) {
15+ TotalUpdates = totalUpdates;
16+ VoidChunks = voidChunks;
17+ Delta = delta;
18+ CurrentState = currently;
19+ }
20+
21+ public void FromBytes(BinaryReader stream) {
22+ TotalUpdates = stream.ReadUInt32();
23+ VoidChunks = stream.ReadUInt32();
24+ Delta = stream.ReadUInt32();
25+ CurrentState = (RunState) stream.ReadByte();
26+ }
27+
28+ public int GetAttributeId() {
29+ return 12345;
30+ }
31+
32+ public object GetValue() {
33+ return this;
34+ }
35+
36+ public void ToBytes(BinaryWriter stream) {
37+ stream.Write(TotalUpdates);
38+ stream.Write(VoidChunks);
39+ stream.Write(Delta);
40+ stream.Write((byte) CurrentState);
41+ }
42+
43+ public string ToJsonToken() {
44+ return $"TotalUpdate:{TotalUpdates}, VoidChunks:{VoidChunks}, Delta:{Delta}";
45+ }
46+
47+ public bool Equals(IWorldAccessor worldForResolve, IAttribute attr) {
48+ StatusData other = attr.GetValue() as StatusData;
49+
50+ if (this.TotalUpdates == other.TotalUpdates &&
51+ this.VoidChunks == other.VoidChunks &&
52+ this.Delta == other.Delta &&
53+ this.CurrentState == other.CurrentState) {
54+ return true;
55+ }
56+ return false;
57+ }
58+ }
59+}
60+
--- a/Automap/Designators/DefaultDesignators.cs
+++ b/Automap/Designators/DefaultDesignators.cs
@@ -55,7 +55,7 @@ namespace Automap
5555 new EntityDesignator(
5656 new AssetLocation("game", "humanoid-trader"),
5757 Color.LightGoldenrodYellow,
58- EnumEntityState.Active,
58+ EnumEntityState.Active,
5959 KeepTrackOfMerchant
6060 );
6161
@@ -63,9 +63,9 @@ namespace Automap
6363 /// Not just blocks, but block-entities as well!
6464 /// </summary>
6565 /// <returns>The block designators.</returns>
66- public static List<BlockDesignator> DefaultBlockDesignators( )
66+ public static List<BlockDesignator> DefaultBlockDesignators()
6767 {
68- return new List<BlockDesignator>{
68+ return new List<BlockDesignator>{
6969 DefaultDesignators.Roads,
7070 DefaultDesignators.GroundSigns,
7171 DefaultDesignators.WallSigns,
@@ -74,7 +74,7 @@ namespace Automap
7474 };
7575 }
7676
77- public static List<EntityDesignator> DefaultEntityDesignators( )
77+ public static List<EntityDesignator> DefaultEntityDesignators()
7878 {
7979 return new List<EntityDesignator>{
8080 DefaultDesignators.Traders,
@@ -83,82 +83,92 @@ namespace Automap
8383
8484 internal static void DecodeSign(ICoreClientAPI clientAPI, PointsOfInterest poi, BlockPos posn, Block block)
8585 {
86- clientAPI.Logger.VerboseDebug("Sign Designator Invoked!");
87- //sign Text into a POI field...
88- BlockEntitySign signEntity = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntitySign;
89-
90- if (signEntity != null && !String.IsNullOrEmpty(signEntity.text))
91- {
92-
93- poi.AddReplace(
94- new PointOfInterest {
95- Location = posn.Copy( ),
96- Notes = signEntity.text,
97- Timestamp = DateTimeOffset.UtcNow,
98- }
99- );
100-
101- }
86+#if DEBUG
87+ clientAPI.Logger.VerboseDebug("Sign Designator Invoked!");
88+#endif
89+ //sign Text into a POI field...
90+ BlockEntitySign signEntity = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntitySign;
91+
92+ if (signEntity != null && !String.IsNullOrEmpty(signEntity.text))
93+ {
94+
95+ poi.AddReplace(
96+ new PointOfInterest
97+ {
98+ Location = posn.Copy(),
99+ Notes = signEntity.text,
100+ Timestamp = DateTimeOffset.UtcNow,
101+ }
102+ );
103+
104+ }
102105
103106 }
104107
105108
106109 internal static void DecodePostSign(ICoreClientAPI clientAPI, PointsOfInterest poi, BlockPos posn, Block block)
107110 {
108- clientAPI.Logger.VerboseDebug("Post-sign Designator Invoked!");
109- //sign post Text into a POI field...
110- BlockEntitySignPost signEntity = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntitySignPost;
111-
112- if (signEntity != null && signEntity.textByCardinalDirection?.Length > 0 ) {
113-
114- poi.AddReplace(
115- new PointOfInterest {
116- Location = posn.Copy( ),
117- Notes = string.Join(",", signEntity.textByCardinalDirection),
118- Timestamp = DateTimeOffset.UtcNow,
119- }
120- );
121-
122- }
111+#if DEBUG
112+ clientAPI.Logger.VerboseDebug("Post-sign Designator Invoked!");
113+#endif
114+ //sign post Text into a POI field...
115+ BlockEntitySignPost signEntity = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntitySignPost;
116+
117+ if (signEntity != null && signEntity.textByCardinalDirection?.Length > 0)
118+ {
119+
120+ poi.AddReplace(
121+ new PointOfInterest
122+ {
123+ Location = posn.Copy(),
124+ Notes = string.Join(",", signEntity.textByCardinalDirection),
125+ Timestamp = DateTimeOffset.UtcNow,
126+ }
127+ );
128+
129+ }
123130 }
124131
125132 internal static void KeepTrackOfMerchant(ICoreClientAPI clientAPI, EntitiesOfInterest poi, BlockPos posn, Entity entity)
126133 {
127- clientAPI.Logger.VerboseDebug("Trader: {0} @ {1}", entity.GetName( ), posn);
128-
129- var message = $"{entity.GetName( )}";
130- var traderJoe = entity as EntityTrader;
131- if (traderJoe.TradeProps != null) {
132- message = $"{traderJoe.GetName( )} Alive:{traderJoe.Alive} - Gears: {traderJoe.TradeProps.Money}, ";
133- }
134- poi.Upsert(entity, message);
134+ clientAPI.Logger.VerboseDebug("Trader: {0} @ {1}", entity.GetName(), posn);
135+
136+ var message = $"{entity.GetName()}";
137+ var traderJoe = entity as EntityTrader;
138+ if (traderJoe.TradeProps != null)
139+ {
140+ message = $"{traderJoe.GetName()} Alive:{traderJoe.Alive} - Gears: {traderJoe.TradeProps.Money}, ";
141+ }
142+ poi.Upsert(entity, message);
135143 }
136144
137145 internal static void DecodeTranslocator(ICoreClientAPI clientAPI, PointsOfInterest poi, BlockPos posn, Block block)
138146 {
139- clientAPI.Logger.VerboseDebug("TRANSLOCATOR Designator Invoked!");
140- //Where to? and from!
141-
142- BlockEntityStaticTranslocator te = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntityStaticTranslocator;
147+ clientAPI.Logger.VerboseDebug("TRANSLOCATOR Designator Invoked!");
148+ //Where to? and from!
143149
144- if (te != null ) {
150+ BlockEntityStaticTranslocator te = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntityStaticTranslocator;
145151
146- StringBuilder textTarget = new StringBuilder( );
147- //translocatorEntity.GetBlockInfo(clientAPI.World.Player, textTarget);
152+ if (te != null)
153+ {
148154
149- textTarget.Append(te.Activated ? "Online " : "offline ");
150- textTarget.Append(" Dest.: ");
151- textTarget.Append(te.TargetLocation != null ? te.TargetLocation.PrettyCoords(clientAPI) : "???");//Or ABS coords?
155+ StringBuilder textTarget = new StringBuilder();
156+ //translocatorEntity.GetBlockInfo(clientAPI.World.Player, textTarget);
152157
153- poi.AddReplace(
154- new PointOfInterest {
155- Location = posn.Copy( ),
156- Notes = textTarget.ToString(),
157- Timestamp = DateTimeOffset.UtcNow,
158- }
159- );
158+ textTarget.Append(te.Activated ? "Online " : "offline ");
159+ textTarget.Append(" Dest.: ");
160+ textTarget.Append(te.TargetLocation != null ? te.TargetLocation.PrettyCoords(clientAPI) : "???");//Or ABS coords?
160161
161- }
162+ poi.AddReplace(
163+ new PointOfInterest
164+ {
165+ Location = posn.Copy(),
166+ Notes = textTarget.ToString(),
167+ Timestamp = DateTimeOffset.UtcNow,
168+ }
169+ );
170+
171+ }
162172 }
163173 }
164174 }
--- a/Automap/Renderers/AlternateRenderer.cs
+++ b/Automap/Renderers/AlternateRenderer.cs
@@ -9,127 +9,127 @@ using Vintagestory.API.MathTools;
99
1010 namespace Automap
1111 {
12- public class AlternateRenderer : IChunkRenderer
13- {
14- private readonly int chunkSize;
15-
16-
17- /// <summary>
18- /// V.G.D:'s Alternative renderer
19- /// </summary>
20- /// <param name="clientAPI">Client API.</param>
21- /// <param name="logger">Logger.</param>
22- public AlternateRenderer(ICoreClientAPI clientAPI, ILogger logger) : base(clientAPI, logger)
23- {
24- chunkSize = ClientAPI.World.BlockAccessor.ChunkSize;
25- }
26-
27- public override void GenerateChunkPngShard(Vec2i chunkPos, IMapChunk mc, ColumnMeta metaData, PngWriter pngWriter, out uint pixelCount)
28- {
29- pixelCount = 0;
30- BlockPos tmpPos = new BlockPos();
31- Vec2i localpos = new Vec2i();
32- var chunksColumn = new IWorldChunk[ClientAPI.World.BlockAccessor.MapSizeY / chunkSize];
33-
34- int topChunkY = mc.YMax / chunkSize;//Heywaitaminute -- this isn't a highest FEATURE, if Rainmap isn't accurate!
35- //Metadata of DateTime chunk was edited, chunk coords.,world-seed? Y-Max feature height
36- //Grab a chunk COLUMN... Topmost Y down...
37- for (int chunkY = 0; chunkY <= topChunkY; chunkY++)
38- {
39- chunksColumn[chunkY] = ClientAPI.World.BlockAccessor.GetChunk(chunkPos.X, chunkY, chunkPos.Y);
40- //What to do if chunk is a void? invalid?
41- }
42-
43- //pre-create PNG line slices...
44- ImageLine[] lines = Enumerable.Repeat(new object(), chunkSize).Select(l => new ImageLine(pngWriter.ImgInfo)).ToArray();
45- ushort[] allMapYs = mc.RainHeightMap;
46- for (int posIndex = 0; posIndex < (chunkSize * chunkSize); posIndex++)
47- {
48- int currY = allMapYs[posIndex];
49- int localChunkY = currY / chunkSize;
50- if (localChunkY >= (chunksColumn.Length)) continue; //Out of range!
51- if (chunksColumn[localChunkY] == null) continue;
52-
53- MapUtil.PosInt2d(posIndex, chunkSize, localpos);
54- int localX = localpos.X;
55- int localZ = localpos.Y;
56-
57- chunksColumn[localChunkY].Unpack();
58- int blockId = chunksColumn[localChunkY].Blocks[MapUtil.Index3d(localX, currY % chunkSize, localZ, chunkSize, chunkSize)];
59-
60- Block block = ClientAPI.World.Blocks[blockId];
61-
62- tmpPos.Set(chunkSize * chunkPos.X + localX, currY, chunkSize * chunkPos.Y + localZ);
63-
64- int red;
65- int green;
66- int blue;
67-
68- //============ POI Population =================
69- if (BlockID_Designators.ContainsKey(blockId))
70- {
71- var desig = BlockID_Designators[blockId];
72- red = desig.OverwriteColor.R;
73- green = desig.OverwriteColor.G;
74- blue = desig.OverwriteColor.B;
75-
76-
77- ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue);
78- continue;
79- }
80-
81- float b = GetSlope(localX, localZ, allMapYs);
82-
83- int col = block.GetColor(ClientAPI, tmpPos);
84- int packedFormat = ColorUtil.ColorMultiply3Clamped(col, b);
85-
86- red = ColorUtil.ColorB(packedFormat);
87- green = ColorUtil.ColorG(packedFormat);
88- blue = ColorUtil.ColorR(packedFormat);
89- ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue);
90-
91- //chunkImage.SetPixel(localX, localZ, pixelColor);
92- pixelCount++;
93- }
94-
95- for (int row = 0; row < pngWriter.ImgInfo.Rows; row++)
96- {
97- pngWriter.WriteRow(lines[row], row);
98- }
99-
100- pngWriter.End();
101- }
102-
103- private float GetSlope(int x, int y, ushort[] heightMap)
104- {
105- int baseY = heightMap[MapUtil.Index2d(x, y, chunkSize)];
106- float runningY = 0;
107- // check bounds. i hate this.
108- int locIndex;
109- if (x > 0)
110- {
111- locIndex = MapUtil.Index2d(x - 1, y, chunkSize);
112- runningY += (baseY - heightMap[locIndex]);
113- }
114- if (x < chunkSize - 1)
115- {
116- locIndex = MapUtil.Index2d(x + 1, y, chunkSize);
117- runningY += (baseY - heightMap[locIndex]);
118- }
119- if (y > 0)
120- {
121- locIndex = MapUtil.Index2d(x, y - 1, chunkSize);
122- runningY += (baseY - heightMap[locIndex]);
123- }
124- if (y < chunkSize - 1)
125- {
126- locIndex = MapUtil.Index2d(x, y + 1, chunkSize);
127- runningY += (baseY - heightMap[locIndex]);
128- }
129- runningY /= 4; // average now
130- runningY /= 5; // idk
131- return 1 + runningY;
132- }
133- }
12+ public class AlternateRenderer : IChunkRenderer
13+ {
14+ private readonly int chunkSize;
15+
16+
17+ /// <summary>
18+ /// V.G.D:'s Alternative renderer
19+ /// </summary>
20+ /// <param name="clientAPI">Client API.</param>
21+ /// <param name="logger">Logger.</param>
22+ public AlternateRenderer(ICoreClientAPI clientAPI, ILogger logger) : base(clientAPI, logger)
23+ {
24+ chunkSize = ClientAPI.World.BlockAccessor.ChunkSize;
25+ }
26+
27+ public override void GenerateChunkPngShard(Vec2i chunkPos, IMapChunk mc, ColumnMeta metaData, PngWriter pngWriter, out uint pixelCount)
28+ {
29+ pixelCount = 0;
30+ BlockPos tmpPos = new BlockPos();
31+ Vec2i localpos = new Vec2i();
32+ var chunksColumn = new IWorldChunk[ClientAPI.World.BlockAccessor.MapSizeY / chunkSize];
33+
34+ int topChunkY = mc.YMax / chunkSize;//Heywaitaminute -- this isn't a highest FEATURE, if Rainmap isn't accurate!
35+ //Metadata of DateTime chunk was edited, chunk coords.,world-seed? Y-Max feature height
36+ //Grab a chunk COLUMN... Topmost Y down...
37+ for (int chunkY = 0; chunkY <= topChunkY; chunkY++)
38+ {
39+ chunksColumn[chunkY] = ClientAPI.World.BlockAccessor.GetChunk(chunkPos.X, chunkY, chunkPos.Y);
40+ //What to do if chunk is a void? invalid?
41+ }
42+
43+ //pre-create PNG line slices...
44+ ImageLine[] lines = Enumerable.Repeat(new object(), chunkSize).Select(l => new ImageLine(pngWriter.ImgInfo)).ToArray();
45+ ushort[] allMapYs = mc.RainHeightMap;
46+ for (int posIndex = 0; posIndex < (chunkSize * chunkSize); posIndex++)
47+ {
48+ int currY = allMapYs[posIndex];
49+ int localChunkY = currY / chunkSize;
50+ if (localChunkY >= (chunksColumn.Length)) continue; //Out of range!
51+ if (chunksColumn[localChunkY] == null) continue;
52+
53+ MapUtil.PosInt2d(posIndex, chunkSize, localpos);
54+ int localX = localpos.X;
55+ int localZ = localpos.Y;
56+
57+ chunksColumn[localChunkY].Unpack();
58+ int blockId = chunksColumn[localChunkY].Blocks[MapUtil.Index3d(localX, currY % chunkSize, localZ, chunkSize, chunkSize)];
59+
60+ Block block = ClientAPI.World.Blocks[blockId];
61+
62+ tmpPos.Set(chunkSize * chunkPos.X + localX, currY, chunkSize * chunkPos.Y + localZ);
63+
64+ int red;
65+ int green;
66+ int blue;
67+
68+ //============ POI Population =================
69+ if (BlockID_Designators.ContainsKey(blockId))
70+ {
71+ var desig = BlockID_Designators[blockId];
72+ red = desig.OverwriteColor.R;
73+ green = desig.OverwriteColor.G;
74+ blue = desig.OverwriteColor.B;
75+
76+
77+ ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue);
78+ continue;
79+ }
80+
81+ float b = GetSlope(localX, localZ, allMapYs);
82+
83+ int col = block.GetColor(ClientAPI, tmpPos);
84+ int packedFormat = ColorUtil.ColorMultiply3Clamped(col, b);
85+
86+ red = ColorUtil.ColorB(packedFormat);
87+ green = ColorUtil.ColorG(packedFormat);
88+ blue = ColorUtil.ColorR(packedFormat);
89+ ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue);
90+
91+ //chunkImage.SetPixel(localX, localZ, pixelColor);
92+ pixelCount++;
93+ }
94+
95+ for (int row = 0; row < pngWriter.ImgInfo.Rows; row++)
96+ {
97+ pngWriter.WriteRow(lines[row], row);
98+ }
99+
100+ pngWriter.End();
101+ }
102+
103+ private float GetSlope(int x, int y, ushort[] heightMap)
104+ {
105+ int baseY = heightMap[MapUtil.Index2d(x, y, chunkSize)];
106+ float runningY = 0;
107+ // check bounds. i hate this.
108+ int locIndex;
109+ if (x > 0)
110+ {
111+ locIndex = MapUtil.Index2d(x - 1, y, chunkSize);
112+ runningY += (baseY - heightMap[locIndex]);
113+ }
114+ if (x < chunkSize - 1)
115+ {
116+ locIndex = MapUtil.Index2d(x + 1, y, chunkSize);
117+ runningY += (baseY - heightMap[locIndex]);
118+ }
119+ if (y > 0)
120+ {
121+ locIndex = MapUtil.Index2d(x, y - 1, chunkSize);
122+ runningY += (baseY - heightMap[locIndex]);
123+ }
124+ if (y < chunkSize - 1)
125+ {
126+ locIndex = MapUtil.Index2d(x, y + 1, chunkSize);
127+ runningY += (baseY - heightMap[locIndex]);
128+ }
129+ runningY /= 4; // average now
130+ runningY /= 5; // idk
131+ return 1 + runningY;
132+ }
133+ }
134134 }
135135
--- a/Automap/Renderers/IChunkRenderer.cs
+++ b/Automap/Renderers/IChunkRenderer.cs
@@ -15,11 +15,11 @@ namespace Automap
1515 public virtual ILogger Logger { get; protected set; }
1616 public virtual Dictionary<int, BlockDesignator> BlockID_Designators { get; set; }
1717
18- protected IChunkRenderer(ICoreClientAPI clientAPI, ILogger logger)
19- {
20- this.ClientAPI = clientAPI;
21- this.Logger = logger;
22- }
18+ protected IChunkRenderer(ICoreClientAPI clientAPI, ILogger logger)
19+ {
20+ this.ClientAPI = clientAPI;
21+ this.Logger = logger;
22+ }
2323
2424 public abstract void GenerateChunkPngShard(Vec2i chunkPos, IMapChunk mapChunk, ColumnMeta metaData, PngWriter pngWriter, out uint pixelCount);
2525 }
--- a/Automap/Renderers/StandardRenerer.cs
+++ b/Automap/Renderers/StandardRenerer.cs
@@ -9,153 +9,153 @@ using Vintagestory.API.MathTools;
99
1010 namespace Automap
1111 {
12- public class StandardRenderer : IChunkRenderer
13- {
14- private readonly int chunkSize;
15-
16-
17- /// <summary>
18- /// Renders shards similar to the Default VS version, plus P.O.I. markings.
19- /// </summary>
20- /// <param name="clientAPI">Client API.</param>
21- /// <param name="logger">Logger.</param>
22- public StandardRenderer(ICoreClientAPI clientAPI, ILogger logger) : base(clientAPI, logger)
23- {
24- chunkSize = ClientAPI.World.BlockAccessor.ChunkSize;
25- }
26-
27- public override void GenerateChunkPngShard(Vec2i chunkPos, IMapChunk mc, ColumnMeta metaData, PngWriter pngWriter, out uint pixelCount)
28- {
29- pixelCount = 0;
30- BlockPos tmpPos = new BlockPos();
31- Vec2i localpos = new Vec2i();
32-
33- var chunksColumn = new IWorldChunk[ClientAPI.World.BlockAccessor.MapSizeY / chunkSize];
34-
35- int topChunkY = mc.YMax / chunkSize;//Heywaitaminute -- this isn't a highest FEATURE, if Rainmap isn't accurate!
36-
37- for (int chunkY = 0; chunkY <= topChunkY; chunkY++)
38- {
39- chunksColumn[chunkY] = ClientAPI.World.BlockAccessor.GetChunk(chunkPos.X, chunkY, chunkPos.Y);
40- //What to do if chunk is a void? invalid?
41- }
42-
43- // Prefetch map chunks, in pattern
44- IMapChunk[] mapChunks = new IMapChunk[]
45- {
46- ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y - 1),
47- ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y),
48- ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X, chunkPos.Y - 1)
49- };
50-
51- //pre-create PNG line slices...
52- ImageLine[] lines = Enumerable.Repeat(new object(), chunkSize).Select(l => new ImageLine(pngWriter.ImgInfo)).ToArray();
53-
54- for (int posIndex = 0; posIndex < (chunkSize * chunkSize); posIndex++)
55- {
56- int mapY = mc.RainHeightMap[posIndex];
57- int localChunkY = mapY / chunkSize;
58- if (localChunkY >= (chunksColumn.Length)) continue;//Out of range!
59-
60- MapUtil.PosInt2d(posIndex, chunkSize, localpos);
61- int localX = localpos.X;
62- int localZ = localpos.Y;
63-
64- float b = 1;
65- int leftTop, rightTop, leftBot;
66-
67- IMapChunk leftTopMapChunk = mc;
68- IMapChunk rightTopMapChunk = mc;
69- IMapChunk leftBotMapChunk = mc;
70-
71- int topX = localX - 1;
72- int botX = localX;
73- int leftZ = localZ - 1;
74- int rightZ = localZ;
75-
76- if (topX < 0 && leftZ < 0)
77- {
78- leftTopMapChunk = mapChunks[0];
79- rightTopMapChunk = mapChunks[1];
80- leftBotMapChunk = mapChunks[2];
81- }
82- else
83- {
84- if (topX < 0)
85- {
86- leftTopMapChunk = mapChunks[1];
87- rightTopMapChunk = mapChunks[1];
88- }
89- if (leftZ < 0)
90- {
91- leftTopMapChunk = mapChunks[2];
92- leftBotMapChunk = mapChunks[2];
93- }
94- }
95-
96- topX = GameMath.Mod(topX, chunkSize);
97- leftZ = GameMath.Mod(leftZ, chunkSize);
98-
99- leftTop = leftTopMapChunk == null ? 0 : Math.Sign(mapY - leftTopMapChunk.RainHeightMap[leftZ * chunkSize + topX]);
100- rightTop = rightTopMapChunk == null ? 0 : Math.Sign(mapY - rightTopMapChunk.RainHeightMap[rightZ * chunkSize + topX]);
101- leftBot = leftBotMapChunk == null ? 0 : Math.Sign(mapY - leftBotMapChunk.RainHeightMap[leftZ * chunkSize + botX]);
102-
103- float slopeness = (leftTop + rightTop + leftBot);
104-
105- if (slopeness > 0) b = 1.2f;
106- if (slopeness < 0) b = 0.8f;
107-
108- b -= 0.15f; //Slope boost value
109-
110- if (chunksColumn[localChunkY] == null)
111- {
112-
113- continue;
114- }
115-
116- chunksColumn[localChunkY].Unpack();
117- int blockId = chunksColumn[localChunkY].Blocks[MapUtil.Index3d(localpos.X, mapY % chunkSize, localpos.Y, chunkSize, chunkSize)];
118-
119- Block block = ClientAPI.World.Blocks[blockId];
120-
121- tmpPos.Set(chunkSize * chunkPos.X + localpos.X, mapY, chunkSize * chunkPos.Y + localpos.Y);
122-
123- int avgCol = block.GetColor(ClientAPI, tmpPos);
124- int rndCol = block.GetRandomColor(ClientAPI, tmpPos, BlockFacing.UP);
125- int col = ColorUtil.ColorOverlay(avgCol, rndCol, 0.125f);
126- var packedFormat = ColorUtil.ColorMultiply3Clamped(col, b);
127-
128- int red = ColorUtil.ColorB(packedFormat);
129- int green = ColorUtil.ColorG(packedFormat);
130- int blue = ColorUtil.ColorR(packedFormat);
131-
132-
133- //============ POI Population =================
134- if (BlockID_Designators.ContainsKey(blockId))
135- {
136- var desig = BlockID_Designators[blockId];
137-
138- if (desig.Enabled)
139- {
140- red = desig.OverwriteColor.R;
141- green = desig.OverwriteColor.G;
142- blue = desig.OverwriteColor.B;
143- }
144- }
145-
146- ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue);
147-
148- //chunkImage.SetPixel(localX, localZ, pixelColor);
149- pixelCount++;
150- }
151-
152- for (int row = 0; row < pngWriter.ImgInfo.Rows; row++)
153- {
154- pngWriter.WriteRow(lines[row], row);
155- }
156-
157- pngWriter.End();
158- }
159- }
12+ public class StandardRenderer : IChunkRenderer
13+ {
14+ private readonly int chunkSize;
15+
16+
17+ /// <summary>
18+ /// Renders shards similar to the Default VS version, plus P.O.I. markings.
19+ /// </summary>
20+ /// <param name="clientAPI">Client API.</param>
21+ /// <param name="logger">Logger.</param>
22+ public StandardRenderer(ICoreClientAPI clientAPI, ILogger logger) : base(clientAPI, logger)
23+ {
24+ chunkSize = ClientAPI.World.BlockAccessor.ChunkSize;
25+ }
26+
27+ public override void GenerateChunkPngShard(Vec2i chunkPos, IMapChunk mc, ColumnMeta metaData, PngWriter pngWriter, out uint pixelCount)
28+ {
29+ pixelCount = 0;
30+ BlockPos tmpPos = new BlockPos();
31+ Vec2i localpos = new Vec2i();
32+
33+ var chunksColumn = new IWorldChunk[ClientAPI.World.BlockAccessor.MapSizeY / chunkSize];
34+
35+ int topChunkY = mc.YMax / chunkSize;//Heywaitaminute -- this isn't a highest FEATURE, if Rainmap isn't accurate!
36+
37+ for (int chunkY = 0; chunkY <= topChunkY; chunkY++)
38+ {
39+ chunksColumn[chunkY] = ClientAPI.World.BlockAccessor.GetChunk(chunkPos.X, chunkY, chunkPos.Y);
40+ //What to do if chunk is a void? invalid?
41+ }
42+
43+ // Prefetch map chunks, in pattern
44+ IMapChunk[] mapChunks = new IMapChunk[]
45+ {
46+ ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y - 1),
47+ ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y),
48+ ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X, chunkPos.Y - 1)
49+ };
50+
51+ //pre-create PNG line slices...
52+ ImageLine[] lines = Enumerable.Repeat(new object(), chunkSize).Select(l => new ImageLine(pngWriter.ImgInfo)).ToArray();
53+
54+ for (int posIndex = 0; posIndex < (chunkSize * chunkSize); posIndex++)
55+ {
56+ int mapY = mc.RainHeightMap[posIndex];
57+ int localChunkY = mapY / chunkSize;
58+ if (localChunkY >= (chunksColumn.Length)) continue;//Out of range!
59+
60+ MapUtil.PosInt2d(posIndex, chunkSize, localpos);
61+ int localX = localpos.X;
62+ int localZ = localpos.Y;
63+
64+ float b = 1;
65+ int leftTop, rightTop, leftBot;
66+
67+ IMapChunk leftTopMapChunk = mc;
68+ IMapChunk rightTopMapChunk = mc;
69+ IMapChunk leftBotMapChunk = mc;
70+
71+ int topX = localX - 1;
72+ int botX = localX;
73+ int leftZ = localZ - 1;
74+ int rightZ = localZ;
75+
76+ if (topX < 0 && leftZ < 0)
77+ {
78+ leftTopMapChunk = mapChunks[0];
79+ rightTopMapChunk = mapChunks[1];
80+ leftBotMapChunk = mapChunks[2];
81+ }
82+ else
83+ {
84+ if (topX < 0)
85+ {
86+ leftTopMapChunk = mapChunks[1];
87+ rightTopMapChunk = mapChunks[1];
88+ }
89+ if (leftZ < 0)
90+ {
91+ leftTopMapChunk = mapChunks[2];
92+ leftBotMapChunk = mapChunks[2];
93+ }
94+ }
95+
96+ topX = GameMath.Mod(topX, chunkSize);
97+ leftZ = GameMath.Mod(leftZ, chunkSize);
98+
99+ leftTop = leftTopMapChunk == null ? 0 : Math.Sign(mapY - leftTopMapChunk.RainHeightMap[leftZ * chunkSize + topX]);
100+ rightTop = rightTopMapChunk == null ? 0 : Math.Sign(mapY - rightTopMapChunk.RainHeightMap[rightZ * chunkSize + topX]);
101+ leftBot = leftBotMapChunk == null ? 0 : Math.Sign(mapY - leftBotMapChunk.RainHeightMap[leftZ * chunkSize + botX]);
102+
103+ float slopeness = (leftTop + rightTop + leftBot);
104+
105+ if (slopeness > 0) b = 1.2f;
106+ if (slopeness < 0) b = 0.8f;
107+
108+ b -= 0.15f; //Slope boost value
109+
110+ if (chunksColumn[localChunkY] == null)
111+ {
112+
113+ continue;
114+ }
115+
116+ chunksColumn[localChunkY].Unpack();
117+ int blockId = chunksColumn[localChunkY].Blocks[MapUtil.Index3d(localpos.X, mapY % chunkSize, localpos.Y, chunkSize, chunkSize)];
118+
119+ Block block = ClientAPI.World.Blocks[blockId];
120+
121+ tmpPos.Set(chunkSize * chunkPos.X + localpos.X, mapY, chunkSize * chunkPos.Y + localpos.Y);
122+
123+ int avgCol = block.GetColor(ClientAPI, tmpPos);
124+ int rndCol = block.GetRandomColor(ClientAPI, tmpPos, BlockFacing.UP);
125+ int col = ColorUtil.ColorOverlay(avgCol, rndCol, 0.125f);
126+ var packedFormat = ColorUtil.ColorMultiply3Clamped(col, b);
127+
128+ int red = ColorUtil.ColorB(packedFormat);
129+ int green = ColorUtil.ColorG(packedFormat);
130+ int blue = ColorUtil.ColorR(packedFormat);
131+
132+
133+ //============ POI Population =================
134+ if (BlockID_Designators.ContainsKey(blockId))
135+ {
136+ var desig = BlockID_Designators[blockId];
137+
138+ if (desig.Enabled)
139+ {
140+ red = desig.OverwriteColor.R;
141+ green = desig.OverwriteColor.G;
142+ blue = desig.OverwriteColor.B;
143+ }
144+ }
145+
146+ ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue);
147+
148+ //chunkImage.SetPixel(localX, localZ, pixelColor);
149+ pixelCount++;
150+ }
151+
152+ for (int row = 0; row < pngWriter.ImgInfo.Rows; row++)
153+ {
154+ pngWriter.WriteRow(lines[row], row);
155+ }
156+
157+ pngWriter.End();
158+ }
159+ }
160160 }
161161
--- a/Automap/Subsystems/AutomapGUIDialog.cs
+++ b/Automap/Subsystems/AutomapGUIDialog.cs
@@ -3,77 +3,154 @@
33
44 using Vintagestory.API.Client;
55 using Vintagestory.API.Common;
6+using Vintagestory.API.Datastructures;
67
78 namespace Automap
89 {
9- public class AutomapGUIDialog : GuiDialog
10- {
11- public const string _automapControlPanelKey = "automapControlPanelKey";
12-
13- private ILogger Logger;
14- private AutomapSystem _automapSystem;
15-
16- public override string ToggleKeyCombinationCode
17- {
18- get
19- {
20- return _automapControlPanelKey;
21- }
22- }
23-
24- public AutomapGUIDialog(ICoreClientAPI capi, AutomapSystem ams) : base(capi)
25- {
26- _automapSystem = ams;
27- Logger = capi.Logger;
28- SetupDialog();
29- }
30-
31- private void SetupDialog()
32- {
33- ElementBounds dialogBounds = ElementStdBounds.AutosizedMainDialog.WithAlignment(EnumDialogArea.CenterMiddle);
34-
35- ElementBounds textBounds = ElementBounds.Fixed(0, 40, 500, 300);
36-
37- ElementBounds bgBounds = ElementBounds.Fill.WithFixedPadding(GuiStyle.ElementToDialogPadding);
38- bgBounds.BothSizing = ElementSizing.FitToChildren;
39- bgBounds.WithChildren(textBounds);
40-
41- ElementBounds toggleBounds = textBounds.CopyOffsetedSibling(3, 5, 5, 2);
42- toggleBounds.fixedHeight = 18;
43- toggleBounds.fixedWidth = 50;
44-
45- ElementBounds txtStatusBounds = textBounds.CopyOffsetedSibling(0, 20, 2, 4);
46- txtStatusBounds.fixedHeight = 16;
47- txtStatusBounds.percentWidth = 1;
48-
49-
50- this.SingleComposer = capi.Gui.CreateCompo("automapControlPanel", dialogBounds)
51- .AddShadedDialogBG(bgBounds)
52- .AddDialogTitleBar("Automap Controls", OnTitleBarCloseClicked)
53- .AddStaticText("Configure Automap settings:", CairoFont.WhiteDetailText(), textBounds)
54- .AddToggleButton("Run", CairoFont.ButtonText(), RunToggle, toggleBounds, "btnRun")
55- .AddStaticText("Idle.", CairoFont.WhiteSmallText().WithFontSize(12), txtStatusBounds, "txtStatus")
56- .Compose();
57- }
58-
59- private void OnTitleBarCloseClicked()
60- {
61- TryClose();
62- }
63-
64- /// <summary>
65- /// Toggle Automap from/to RUN state
66- /// </summary>
67- /// <returns>The toggle.</returns>
68- /// <param name="t1">T1.</param>
69- internal void RunToggle(bool toggle)
70- {
71- _automapSystem.Enabled = toggle;
72- Logger.VerboseDebug("Dialog Changed; [ Automap Enabled: {0} ]", toggle);
73- var statusText = this.SingleComposer.GetStaticText("txtStatus");
74- statusText.SetValue($"Running: {this._automapSystem.Enabled}, Total: {this._automapSystem.updatedChunksTotal}, Nulls: {this._automapSystem.nullChunkCount} ");
75-
76- }
77- }
10+ public class AutomapGUIDialog : GuiDialog
11+ {
12+ public const string _automapControlPanelKey = "automapControlPanelKey";
13+ private const string _statusTextKey = @"txtStatus";
14+ private const string _noteTextKey = @"edtNote";
15+
16+ private ILogger Logger;
17+
18+ private long dashTickHandle;
19+
20+ public override string ToggleKeyCombinationCode
21+ {
22+ get {
23+ return _automapControlPanelKey;
24+ }
25+ }
26+
27+ private uint totalShards, voidShards, changesThisTick;
28+ private RunState lastState;
29+
30+
31+ public AutomapGUIDialog(ICoreClientAPI capi, AutomapSystem ams) : base(capi)
32+ {
33+
34+ Logger = capi.Logger;
35+ SetupDialog();
36+ capi.Event.RegisterEventBusListener(AutomapStatusMsg, 1.0D, AutomapSystem.AutomapStatusEventKey);
37+
38+ }
39+
40+ //Event for GUI status display
41+
42+ private void SetupDialog()
43+ {
44+ ElementBounds dialogBounds = ElementStdBounds.AutosizedMainDialog.WithAlignment(EnumDialogArea.CenterMiddle);
45+
46+ ElementBounds textBounds = ElementBounds.Fixed(0, 40, 500, 300);
47+
48+ ElementBounds bgBounds = ElementBounds.Fill.WithFixedPadding(GuiStyle.ElementToDialogPadding);
49+ bgBounds.BothSizing = ElementSizing.FitToChildren;
50+ bgBounds.WithChildren(textBounds);
51+
52+ ElementBounds toggleBounds = textBounds.CopyOffsetedSibling(0, 72, 5, 2);
53+ toggleBounds.fixedHeight = 24;
54+ toggleBounds.fixedWidth = 64;
55+
56+ ElementBounds txtStatusBounds = textBounds.CopyOffsetedSibling(0, 26, 2, 4);
57+ txtStatusBounds.fixedHeight = 16;
58+ txtStatusBounds.percentWidth = 1;
59+
60+ ElementBounds btnNoteArea = textBounds.CopyOffsetedSibling(0, 42, 2, 5);
61+ btnNoteArea.fixedHeight = 24;
62+ btnNoteArea.fixedWidth = 20;
63+
64+ ElementBounds txtNoteArea = btnNoteArea.CopyOffsetedSibling(64, 0, 1, 6);
65+ txtNoteArea.fixedHeight = 24;
66+ txtNoteArea.fixedWidth = 256;
67+
68+
69+ this.SingleComposer = capi.Gui.CreateCompo("automapControlPanel", dialogBounds)
70+ .AddShadedDialogBG(bgBounds)
71+ .AddDialogTitleBar("Automap Controls", OnTitleBarCloseClicked)
72+ .AddStaticText("Configure Automap settings:", CairoFont.WhiteDetailText(), textBounds)
73+ .AddToggleButton("Run", CairoFont.ButtonText(), RunToggle, toggleBounds, "btnRun")
74+ .AddDynamicText("Idle.", CairoFont.WhiteSmallText().WithFontSize(12), EnumTextOrientation.Left, txtStatusBounds, _statusTextKey)
75+ .AddTextInput(txtNoteArea, null, CairoFont.WhiteMediumText().WithFontSize(16), _noteTextKey)
76+ .AddButton("Note:", CreateNote, btnNoteArea, CairoFont.ButtonText())
77+ .Compose();
78+
79+ //Controls for ALL Block & Entity Designators (Enable/Disable)
80+ //_automapSystem.BlockID_Designators
81+ //_automapSystem.Entity_Designators
82+ //Renderer selection
83+ //Message verbosity? Speed?
84+
85+ //A Button to add POI - notes manually (when AM running)
86+
87+ }
88+
89+ private void OnTitleBarCloseClicked()
90+ {
91+ TryClose();
92+ }
93+
94+ /// <summary>
95+ /// Toggle Automap from/to RUN state
96+ /// </summary>
97+ /// <returns>The toggle.</returns>
98+ /// <param name="toggle">Run.</param>
99+ internal void RunToggle(bool toggle)
100+ {
101+ Logger.VerboseDebug("Dialog Changed; [ Automap Enabled: {0} ]", toggle);
102+ var statusText = this.SingleComposer.GetDynamicText(_statusTextKey);
103+ statusText.SetNewText($"State: {(toggle ? "Run" : "Halt")}, Total: {totalShards}, Nulls: {voidShards} ");
104+
105+ CommandData cmd;
106+
107+ if (toggle)
108+ {
109+ dashTickHandle = capi.Event.RegisterGameTickListener(UpdateDashDisplay, 6001);
110+ cmd = new CommandData(toggle ? RunState.Run : RunState.Stop, new bool[] { true, true, true, true, true });
111+ }
112+ else
113+ {
114+ capi.Event.UnregisterGameTickListener(dashTickHandle);
115+ cmd = new CommandData(toggle ? RunState.Run : RunState.Stop);
116+ }
117+
118+ capi.Event.PushEvent(AutomapSystem.AutomapCommandEventKey, cmd);
119+ }
120+
121+ private bool CreateNote()
122+ {
123+ var noteCmd = new CommandData(RunState.Notation);
124+ var txtNote = this.SingleComposer.GetTextInput(_noteTextKey);
125+ if (!String.IsNullOrWhiteSpace(txtNote.GetText()))
126+ {
127+ noteCmd.Notation = txtNote.GetText();
128+ txtNote.SetValue(string.Empty);
129+
130+ capi.Event.PushEvent(AutomapSystem.AutomapCommandEventKey, noteCmd);
131+ }
132+ return false;//???
133+ }
134+
135+ private void AutomapStatusMsg(string eventName, ref EnumHandling handling, IAttribute data)
136+ {
137+ Logger.VerboseDebug("MsgBus RX: AutomapStatusMsg");
138+ StatusData realData = data as StatusData;
139+ totalShards = realData.TotalUpdates;
140+ voidShards = realData.VoidChunks;
141+ changesThisTick = realData.Delta;
142+ lastState = realData.CurrentState;
143+ }
144+
145+ private void UpdateDashDisplay(float delay)
146+ {
147+ var statusText = this.SingleComposer.GetDynamicText(_statusTextKey);
148+ statusText.SetNewText($"State: {lastState}, Total: {totalShards}, Delta: {changesThisTick} Nulls: {voidShards} ");
149+
150+
151+ }
152+
153+
154+ }
78155 }
79156
--- a/Automap/Subsystems/AutomapSystem.cs
+++ b/Automap/Subsystems/AutomapSystem.cs
@@ -16,617 +16,812 @@ using Hjg.Pngcs.Chunks;
1616 using Vintagestory.API.Client;
1717 using Vintagestory.API.Common;
1818 using Vintagestory.API.Common.Entities;
19+using Vintagestory.API.Config;
20+using Vintagestory.API.Datastructures;
1921 using Vintagestory.API.MathTools;
2022 using Vintagestory.Common;
2123
2224 namespace Automap
2325 {
24- public class AutomapSystem
25- {
26- private Thread cartographer_thread;
27- private ICoreClientAPI ClientAPI { get; set; }
28- private ILogger Logger { get; set; }
29- private IChunkRenderer ChunkRenderer { get; set; }
26+ public class AutomapSystem
27+ {
28+ private Thread cartographer_thread;
29+ private ICoreClientAPI ClientAPI { get; set; }
30+ private ILogger Logger { get; set; }
31+ private IChunkRenderer ChunkRenderer { get; set; }
3032
31- private const string _mapPath = @"Maps";
32- private const string _chunkPath = @"Chunks";
33- private const string _domain = @"automap";
34- private const string chunkFile_filter = @"*_*.png";
35- private static Regex chunkShardRegex = new Regex(@"(?<X>[\d]+)_(?<Z>[\d]+).png", RegexOptions.Singleline);
33+ private const string _mapPath = @"Maps";
34+ private const string _chunkPath = @"Chunks";
35+ private const string _domain = @"automap";
36+ private const string chunkFile_filter = @"*_*.png";
37+ private static Regex chunkShardRegex = new Regex(@"(?<X>[\d]+)_(?<Z>[\d]+).png", RegexOptions.Singleline);
3638
37- private ConcurrentDictionary<Vec2i, uint> columnCounter = new ConcurrentDictionary<Vec2i, uint>(3, 150);
38- private ColumnsMetadata chunkTopMetadata;
39- private PointsOfInterest POIs;
40- private EntitiesOfInterest EOIs;
39+ private ConcurrentDictionary<Vec2i, uint> columnCounter = new ConcurrentDictionary<Vec2i, uint>(3, 150);
40+ private ColumnsMetadata chunkTopMetadata;
41+ private PointsOfInterest POIs = new PointsOfInterest();
42+ private EntitiesOfInterest EOIs = new EntitiesOfInterest();
4143
42- internal Dictionary<int, BlockDesignator> BlockID_Designators { get; private set; }
43- internal Dictionary<AssetLocation, EntityDesignator> Entity_Designators { get; private set; }
44+ internal Dictionary<int, BlockDesignator> BlockID_Designators { get; private set; }
45+ internal Dictionary<AssetLocation, EntityDesignator> Entity_Designators { get; private set; }
46+ internal Dictionary<int, string> RockIdCodes { get; private set; }
4447
45- internal bool Enabled { get; set; }
46- //Run status, Chunks processed, stats, center of map....
47- internal uint nullChunkCount, updatedChunksTotal;
48- internal Vec2i startChunkColumn;
48+ internal RunState CurrentState { get; set; }
49+ //Run status, Chunks processed, stats, center of map....
50+ private uint nullChunkCount, updatedChunksTotal;
51+ private Vec2i startChunkColumn;
4952
50- private readonly int chunkSize;
51- private string path;
52- private IAsset stylesFile;
53+ private readonly int chunkSize;
54+ private string path;
55+ private IAsset stylesFile;
5356
57+ public static string AutomapStatusEventKey = @"AutomapStatus";
58+ public static string AutomapCommandEventKey = @"AutomapCommand";
5459
55- public AutomapSystem(ICoreClientAPI clientAPI, ILogger logger)
56- {
57- this.ClientAPI = clientAPI;
58- this.Logger = logger;
59- chunkSize = ClientAPI.World.BlockAccessor.ChunkSize;
60- ClientAPI.Event.LevelFinalize += EngageAutomap;
6160
62- //TODO:Choose which one from GUI
63- this.ChunkRenderer = new StandardRenderer(clientAPI, logger);
64- }
61+ public AutomapSystem(ICoreClientAPI clientAPI, ILogger logger)
62+ {
63+ this.ClientAPI = clientAPI;
64+ this.Logger = logger;
65+ chunkSize = ClientAPI.World.BlockAccessor.ChunkSize;
66+ ClientAPI.Event.LevelFinalize += EngageAutomap;
6567
68+ //TODO:Choose which one from GUI
69+ this.ChunkRenderer = new StandardRenderer(clientAPI, logger);
6670
67- #region Internals
68- private void EngageAutomap()
69- {
70- path = ClientAPI.GetOrCreateDataPath(_mapPath);
71- path = ClientAPI.GetOrCreateDataPath(Path.Combine(path, "World_" + ClientAPI.World.Seed));//Add name of World too...'ServerApi.WorldManager.CurrentWorldName'
71+ //Listen on bus for commands
72+ ClientAPI.Event.RegisterEventBusListener(CommandListener, 1.0, AutomapSystem.AutomapCommandEventKey);
7273
73- stylesFile = ClientAPI.World.AssetManager.Get(new AssetLocation(_domain, "config/automap_format.css"));
74- Logger.VerboseDebug("CSS loaded: {0} size: {1}", stylesFile.IsLoaded(), stylesFile.ToText().Length);
74+ }
7575
76- Prefill_POI_Designators();
77- startChunkColumn = new Vec2i((ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.X / chunkSize), (ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Z / chunkSize));
78- chunkTopMetadata = new ColumnsMetadata(startChunkColumn);
7976
80- Logger.Notification("AUTOMAP Start {0}", startChunkColumn);
81- Reload_Metadata();
77+ #region Internals
78+ private void EngageAutomap()
79+ {
80+ path = ClientAPI.GetOrCreateDataPath(_mapPath);
81+ path = ClientAPI.GetOrCreateDataPath(Path.Combine(path, "World_" + ClientAPI.World.Seed));//Add name of World too...'ServerApi.WorldManager.CurrentWorldName'
8282
83- ClientAPI.Event.ChunkDirty += ChunkAChanging;
83+ stylesFile = ClientAPI.World.AssetManager.Get(new AssetLocation(_domain, "config/automap_format.css"));
84+ Logger.VerboseDebug("CSS loaded: {0} size: {1}", stylesFile.IsLoaded(), stylesFile.ToText().Length);
8485
85- cartographer_thread = new Thread(Cartographer)
86- {
87- Name = "Cartographer",
88- Priority = ThreadPriority.Lowest,
89- IsBackground = true
90- };
86+ Prefill_POI_Designators();
87+ startChunkColumn = new Vec2i((ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.X / chunkSize), (ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Z / chunkSize));
88+ chunkTopMetadata = new ColumnsMetadata(startChunkColumn);
9189
92- ClientAPI.Event.RegisterGameTickListener(AwakenCartographer, 6000);
93- }
90+ Logger.Notification("AUTOMAP Start {0}", startChunkColumn);
91+ Reload_Metadata();
9492
95- private void ChunkAChanging(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason)
96- {
97- Vec2i topPosition = new Vec2i(chunkCoord.X, chunkCoord.Z);
93+ ClientAPI.Event.ChunkDirty += ChunkAChanging;
9894
99- columnCounter.AddOrUpdate(topPosition, 1, (key, colAct) => colAct + 1);
100- }
95+ cartographer_thread = new Thread(Cartographer)
96+ {
97+ Name = "Cartographer",
98+ Priority = ThreadPriority.Lowest,
99+ IsBackground = true
100+ };
101101
102- private void AwakenCartographer(float delayed)
103- {
102+ ClientAPI.Event.RegisterGameTickListener(AwakenCartographer, 6000);
103+ }
104104
105- if (Enabled && (ClientAPI.IsGamePaused != false || ClientAPI.IsShuttingDown != true))
106- {
105+ private void ChunkAChanging(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason)
106+ {
107+ Vec2i topPosition = new Vec2i(chunkCoord.X, chunkCoord.Z);
108+
109+ columnCounter.AddOrUpdate(topPosition, 1, (key, colAct) => colAct + 1);
110+ }
111+
112+ private void AwakenCartographer(float delayed)
113+ {
114+
115+ if (CurrentState == RunState.Run && (ClientAPI.IsGamePaused != false || ClientAPI.IsShuttingDown != true))
116+ {
107117 #if DEBUG
108- Logger.VerboseDebug("Cartographer re-trigger from [{0}]", cartographer_thread.ThreadState);
118+ Logger.VerboseDebug("Cartographer re-trigger from [{0}]", cartographer_thread.ThreadState);
109119 #endif
110120
111- if (cartographer_thread.ThreadState.HasFlag(ThreadState.Unstarted))
112- {
113- cartographer_thread.Start();
114- }
115- else if (cartographer_thread.ThreadState.HasFlag(ThreadState.WaitSleepJoin))
116- {
117- //Time to (re)write chunk shards
118- cartographer_thread.Interrupt();
119- }
121+ if (cartographer_thread.ThreadState.HasFlag(ThreadState.Unstarted))
122+ {
123+ cartographer_thread.Start();
124+ }
125+ else if (cartographer_thread.ThreadState.HasFlag(ThreadState.WaitSleepJoin))
126+ {
127+ //Time to (re)write chunk shards
128+ cartographer_thread.Interrupt();
129+ }
130+ //#if DEBUG
131+ //ClientAPI.TriggerChatMessage($"Automap {updatedChunksTotal} Updates - MAX (N:{chunkTopMetadata.North_mostChunk},S:{chunkTopMetadata.South_mostChunk},E:{chunkTopMetadata.East_mostChunk}, W:{chunkTopMetadata.West_mostChunk} - TOTAL: {chunkTopMetadata.Count})");
132+ //#endif
133+ }
134+ else if (CurrentState == RunState.Snapshot)
135+ {
136+ //TODO: Snapshot generator second thread...
137+ }
138+
139+ }
140+
141+
142+ private void Cartographer()
143+ {
144+ wake:
145+ Logger.VerboseDebug("Cartographer thread awoken");
146+
147+ try
148+ {
149+ uint ejectedItem = 0;
150+ uint updatedChunks = 0;
151+
152+ //-- Should dodge enumerator changing underfoot....at a cost.
153+ if (!columnCounter.IsEmpty)
154+ {
155+ var tempSet = columnCounter.ToArray().OrderByDescending(kvp => kvp.Value);
156+ foreach (var mostActiveCol in tempSet)
157+ {
158+
159+ var mapChunk = ClientAPI.World.BlockAccessor.GetMapChunk(mostActiveCol.Key);
160+
161+ if (mapChunk == null)
162+ {
163+ Logger.Warning("SKIP CHUNK: ({0}) - Map Chunk NULL!", mostActiveCol.Key);
164+ nullChunkCount++;
165+ columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
166+ continue;
167+ }
168+
169+ ColumnMeta chunkMeta = CreateColumnMetadata(mostActiveCol, mapChunk);
170+ PngWriter pngWriter = SetupPngImage(mostActiveCol.Key, chunkMeta);
171+ UpdateEntityMetadata();
172+ ProcessChunkBlocks(mostActiveCol.Key, mapChunk, chunkMeta);
173+
174+ uint updatedPixels = 0;
175+
176+ ChunkRenderer.GenerateChunkPngShard(mostActiveCol.Key, mapChunk, chunkMeta, pngWriter, out updatedPixels);
177+
178+ if (updatedPixels > 0)
179+ {
180+
120181 #if DEBUG
121- ClientAPI.TriggerChatMessage($"Automap {updatedChunksTotal} Updates - MAX (N:{chunkTopMetadata.North_mostChunk},S:{chunkTopMetadata.South_mostChunk},E:{chunkTopMetadata.East_mostChunk}, W:{chunkTopMetadata.West_mostChunk} - TOTAL: {chunkTopMetadata.Count})");
182+ Logger.VerboseDebug("Wrote chunk shard: ({0}) - Edits#:{1}, Pixels#:{2}", mostActiveCol.Key, mostActiveCol.Value, updatedPixels);
122183 #endif
123- }
184+ updatedChunks++;
185+ chunkTopMetadata.Update(chunkMeta);
186+ columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
187+ }
188+ else
189+ {
190+ columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
191+ Logger.VerboseDebug("Un-painted chunk: ({0}) ", mostActiveCol.Key);
192+ }
124193
125- }
194+ }
195+ }
126196
197+ UpdateStatus(this.updatedChunksTotal, this.nullChunkCount, updatedChunks);
127198
128- private void Cartographer()
129- {
130- wake:
131- Logger.VerboseDebug("Cartographer thread awoken");
199+ if (updatedChunks > 0)
200+ {
201+ //What about chunk updates themselves; a update bitmap isn't kept...
202+ updatedChunksTotal += updatedChunks;
203+ GenerateMapHTML();
204+ GenerateJSONMetadata();
205+ updatedChunks = 0;
206+ }
207+
208+ //Then sleep until interupted again, and repeat
209+
210+ Logger.VerboseDebug("Thread '{0}' about to sleep indefinitely.", Thread.CurrentThread.Name);
211+
212+ Thread.Sleep(Timeout.Infinite);
213+
214+ }
215+ catch (ThreadInterruptedException)
216+ {
217+
218+ Logger.VerboseDebug("Thread '{0}' interupted [awoken]", Thread.CurrentThread.Name);
219+ goto wake;
220+
221+ }
222+ catch (ThreadAbortException)
223+ {
224+ Logger.VerboseDebug("Thread '{0}' aborted.", Thread.CurrentThread.Name);
225+
226+ }
227+ finally
228+ {
229+ Logger.VerboseDebug("Thread '{0}' executing finally block.", Thread.CurrentThread.Name);
230+ }
231+ }
232+
233+ private void UpdateStatus(uint totalUpdates, uint voidChunks, uint delta)
234+ {
235+ StatusData updateData = new StatusData(totalUpdates, voidChunks, delta, RunState.Run);
236+
237+ this.ClientAPI.Event.PushEvent(AutomapStatusEventKey, updateData);
238+ }
239+
240+ private void Prefill_POI_Designators()
241+ {
242+
243+ this.BlockID_Designators = new Dictionary<int, BlockDesignator>();
244+ this.Entity_Designators = new Dictionary<AssetLocation, EntityDesignator>();
245+ this.RockIdCodes = Helpers.ArbitrarytBlockIdHunter(ClientAPI, new AssetLocation(GlobalConstants.DefaultDomain, "rock"), EnumBlockMaterial.Stone);
246+
247+ //Add special marker types for BlockID's of "Interest", overwrite colour, and method
248+
249+ Install_POI_Designators(DefaultDesignators.DefaultBlockDesignators(), DefaultDesignators.DefaultEntityDesignators());
250+ }
251+
252+ private void Install_POI_Designators(ICollection<BlockDesignator> blockDesig, List<EntityDesignator> entDesig)
253+ {
254+ Logger.VerboseDebug("Connecting {0} standard Block-Designators", blockDesig.Count);
255+ foreach (var designator in blockDesig)
256+ {
257+ var blockIDs = Helpers.ArbitrarytBlockIdHunter(ClientAPI, designator.Pattern, designator.Material);
258+ if (blockIDs.Count > 0) { Logger.VerboseDebug("Designator {0} has {1} associated blockIDs", designator.ToString(), blockIDs.Count); }
259+ foreach (var entry in blockIDs)
260+ {
261+ BlockID_Designators.Add(entry.Key, designator);
262+ }
263+ }
264+ this.ChunkRenderer.BlockID_Designators = BlockID_Designators;
265+
266+
267+ Logger.VerboseDebug("Connecting {0} standard Entity-Designators", entDesig.Count);
268+ foreach (var designator in entDesig)
269+ {
270+ //Get Variants first, from EntityTypes...better be populated!
271+ var matched = ClientAPI.World.EntityTypes.FindAll(entp => entp.Code.BeginsWith(designator.Pattern.Domain, designator.Pattern.Path));
272+
273+ foreach (var match in matched)
274+ {
275+ Logger.VerboseDebug("Linked Entity: {0} Designator: {1}", match.Code, designator);
276+ this.Entity_Designators.Add(match.Code, designator);
277+ }
278+
279+
280+
281+ //EntityProperties props = ClientAPI.World.GetEntityType(designator.Pattern);
282+ }
283+
284+
285+ }
286+
287+
288+ private void GenerateMapHTML()
289+ {
290+ string mapFilename = Path.Combine(path, "Automap.html");
291+
292+ int TopNorth = chunkTopMetadata.North_mostChunk;
293+ int TopSouth = chunkTopMetadata.South_mostChunk;
294+ int TopEast = chunkTopMetadata.East_mostChunk;
295+ int TopWest = chunkTopMetadata.West_mostChunk;
296+
297+ using (StreamWriter outputText = new StreamWriter(File.Open(mapFilename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)))
298+ {
299+ using (HtmlTextWriter tableWriter = new HtmlTextWriter(outputText))
300+ {
301+ tableWriter.BeginRender();
302+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Html);
303+
304+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Head);
305+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Title);
306+ tableWriter.WriteEncodedText("Generated Automap");
307+ tableWriter.RenderEndTag();
308+ //CSS style here
309+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Style);
310+ tableWriter.Write(stylesFile.ToText());
311+ tableWriter.RenderEndTag();//</style>
312+
313+ tableWriter.RenderEndTag();
314+
315+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Body);
316+ tableWriter.RenderBeginTag(HtmlTextWriterTag.P);
317+ tableWriter.WriteEncodedText($"Created {DateTimeOffset.UtcNow.ToString("u")}");
318+ tableWriter.RenderEndTag();
319+ tableWriter.RenderBeginTag(HtmlTextWriterTag.P);
320+ tableWriter.WriteEncodedText($"W:{TopWest}, E: {TopEast}, N:{TopNorth}, S:{TopSouth} ");
321+ tableWriter.RenderEndTag();
322+ tableWriter.WriteLine();
323+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Table);
324+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Caption);
325+ tableWriter.WriteEncodedText($"Start: {startChunkColumn}, Seed: {ClientAPI.World.Seed}\n");
326+ tableWriter.RenderEndTag();
327+
328+ //################ X-Axis <thead> #######################
329+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Thead);
330+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr);
331+
332+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Th);
333+ tableWriter.Write("N, W");
334+ tableWriter.RenderEndTag();
335+
336+ for (int xAxisT = TopWest; xAxisT <= TopEast; xAxisT++)
337+ {
338+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Th);
339+ tableWriter.Write(xAxisT);
340+ tableWriter.RenderEndTag();
341+ }
342+
343+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Th);
344+ tableWriter.Write("N, E");
345+ tableWriter.RenderEndTag();
346+
347+ tableWriter.RenderEndTag();
348+ tableWriter.RenderEndTag();
349+ //###### </thead> ################################
350+
351+ //###### <tbody> - Chunk rows & Y-axis cols
352+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Tbody);
353+
354+ //######## <tr> for every vertical row
355+ for (int yAxis = TopNorth; yAxis <= TopSouth; yAxis++)
356+ {
357+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr);
358+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
359+ tableWriter.Write(yAxis);//legend: Y-axis
360+ tableWriter.RenderEndTag();
361+
362+ for (int xAxis = TopWest; xAxis <= TopEast; xAxis++)
363+ {
364+ //###### <td> #### for chunk shard
365+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
366+ var colLoc = new Vec2i(xAxis, yAxis);
367+ if (chunkTopMetadata.Contains(colLoc))
368+ {
369+ ColumnMeta meta = chunkTopMetadata[colLoc];
370+ //Tooltip first
371+ tableWriter.AddAttribute(HtmlTextWriterAttribute.Class, "tooltip");
372+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Div);
373+
374+ tableWriter.AddAttribute(HtmlTextWriterAttribute.Src, $"{xAxis}_{yAxis}.png");
375+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Img);
376+ tableWriter.RenderEndTag();
377+ // <span class="tooltiptext">Tooltip text
378+ tableWriter.AddAttribute(HtmlTextWriterAttribute.Class, "tooltiptext");
379+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Span);
380+
381+ StringBuilder tooltipText = new StringBuilder();
382+ tooltipText.Append($"{meta.Location.PrettyCoords(ClientAPI)} ");
383+ tooltipText.Append($" Max-Height: {meta.YMax}, Temp: {meta.Temperature.ToString("F1")} ");
384+ tooltipText.Append($" Rainfall: {meta.Rainfall.ToString("F1")}, ");
385+ tooltipText.Append($" Shrubs: {meta.ShrubDensity.ToString("F1")}, ");
386+ tooltipText.Append($" Forest: {meta.ForestDensity.ToString("F1")}, ");
387+ tooltipText.Append($" Fertility: {meta.Fertility.ToString("F1")}, ");
388+
389+ if (meta.RockRatio != null)
390+ {
391+ foreach (KeyValuePair<int, uint> blockID in meta.RockRatio)
392+ {
393+ var block = ClientAPI.World.GetBlock(blockID.Key);
394+ tooltipText.AppendFormat(" {0} × {1},\t", block.Code.GetName(), meta.RockRatio[blockID.Key]);
395+ }
396+ }
397+
398+ tableWriter.WriteEncodedText(tooltipText.ToString());
399+
400+ tableWriter.RenderEndTag();//</span>
401+
402+
403+ tableWriter.RenderEndTag();//</div> --tooltip enclosure
404+ }
405+ else
406+ {
407+ tableWriter.Write("?");
408+ }
409+
410+ tableWriter.RenderEndTag();
411+ }//############ </td> ###########
412+
413+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
414+ tableWriter.Write(yAxis);//legend: Y-axis
415+ tableWriter.RenderEndTag();
416+
417+ tableWriter.RenderEndTag();
418+
419+ }
420+ tableWriter.RenderEndTag();
421+
422+ //################ X-Axis <tfoot> #######################
423+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Tfoot);
424+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr);
425+
426+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
427+ tableWriter.Write("S, W");
428+ tableWriter.RenderEndTag();
429+
430+ for (int xAxisB = TopWest; xAxisB <= TopEast; xAxisB++)
431+ {
432+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
433+ tableWriter.Write(xAxisB);
434+ tableWriter.RenderEndTag();
435+ }
436+
437+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
438+ tableWriter.Write("S, E");
439+ tableWriter.RenderEndTag();
440+
441+ tableWriter.RenderEndTag();
442+ tableWriter.RenderEndTag();
443+ //###### </tfoot> ################################
444+
445+
446+ tableWriter.RenderEndTag();//</table>
447+
448+ //############## POI list #####################
449+ tableWriter.RenderBeginTag(HtmlTextWriterTag.P);
450+ tableWriter.WriteLine("Points of Interest");
451+ tableWriter.RenderEndTag();
452+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Ul);
453+ foreach (var poi in this.POIs)
454+ {
455+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Li);
456+ tableWriter.WriteEncodedText(poi.Location.PrettyCoords(this.ClientAPI) + "\t");
457+ tableWriter.WriteEncodedText(poi.Notes + "\t");
458+ tableWriter.WriteEncodedText(poi.Timestamp.ToString("u"));
459+ tableWriter.RenderEndTag();
460+ }
461+
462+ foreach (var eoi in this.EOIs.PointsList)
463+ {
464+ tableWriter.RenderBeginTag(HtmlTextWriterTag.Li);
465+ tableWriter.WriteEncodedText(eoi.Location.PrettyCoords(this.ClientAPI) + "\t");
466+ tableWriter.WriteEncodedText(eoi.Notes + "\t");
467+ tableWriter.WriteEncodedText(eoi.Timestamp.ToString("u"));
468+ tableWriter.RenderEndTag();
469+ }
470+
471+ tableWriter.RenderEndTag();
472+
473+
474+
475+
476+ tableWriter.RenderEndTag();//### </BODY> ###
477+
478+ tableWriter.EndRender();
479+ tableWriter.Flush();
480+ }
481+ outputText.Flush();
482+ }
483+
484+ Logger.VerboseDebug("Generated HTML map");
485+ }
486+
487+ /// <summary>
488+ /// Generates the JSON Metadata. (in MAP object format )
489+ /// </summary>
490+ private void GenerateJSONMetadata()
491+ {
492+ string jsonFilename = Path.Combine(path, "Metadata.js");
493+
494+ StreamWriter jsonWriter = new StreamWriter(jsonFilename, false, Encoding.UTF8);
495+ using (jsonWriter)
496+ {
497+ jsonWriter.Write("ViewFrame.chunks={};");
498+ jsonWriter.Write("ViewFrame.chunks.worldSeedNum={0};", ClientAPI.World.Seed);
499+ jsonWriter.Write("ViewFrame.chunks.genTime=new Date('{0}');", DateTimeOffset.UtcNow.ToString("O"));
500+ jsonWriter.Write("ViewFrame.chunks.startCoords=[{0},{1}];", startChunkColumn.X, startChunkColumn.Y);
501+ jsonWriter.Write("ViewFrame.chunks.chunkSize={0};", chunkSize);
502+ jsonWriter.Write("ViewFrame.chunks.northMostChunk={0};", chunkTopMetadata.North_mostChunk);
503+ jsonWriter.Write("ViewFrame.chunks.southMostChunk={0};", chunkTopMetadata.South_mostChunk);
504+ jsonWriter.Write("ViewFrame.chunks.eastMostChunk={0};", chunkTopMetadata.East_mostChunk);
505+ jsonWriter.Write("ViewFrame.chunks.westMostChunk={0};", chunkTopMetadata.West_mostChunk);
506+ //MAP object format - [key, value]: key is "x_y"
507+ jsonWriter.Write("ViewFrame.chunks.chunkMetadata=new Map([");
508+ foreach (var shard in chunkTopMetadata)
509+ {
510+ jsonWriter.Write("['{0}_{1}',", shard.Location.X, shard.Location.Y);
511+ jsonWriter.Write("{");
512+ jsonWriter.Write("prettyCoord:'{0}',", shard.Location.PrettyCoords(ClientAPI));
513+ jsonWriter.Write("chunkAge:'{0}',", shard.ChunkAge.ToString("g"));//World age - relative? or last edit ??
514+ jsonWriter.Write("temp:{0},", shard.Temperature.ToString("F1"));
515+ jsonWriter.Write("YMax:{0},", shard.YMax);
516+ jsonWriter.Write("fert:{0},", shard.Fertility.ToString("F1"));
517+ jsonWriter.Write("forestDens:{0},", shard.ForestDensity.ToString("F1"));
518+ jsonWriter.Write("rain:{0},", shard.Rainfall.ToString("F1"));
519+ jsonWriter.Write("shrubDens:{0},", shard.ShrubDensity.ToString("F1"));
520+ jsonWriter.Write("airBlocks:{0},", shard.AirBlocks);
521+ jsonWriter.Write("nonAirBlocks:{0},", shard.NonAirBlocks);
522+ //TODO: Heightmap
523+ //TODO: Rock-ratio
524+ jsonWriter.Write("}],");
525+ }
526+ jsonWriter.Write("]);");
527+
528+
529+ jsonWriter.Write("ViewFrame.chunks.pointsOfInterest = new Map([");
530+ foreach (var poi in POIs)
531+ {
532+ jsonWriter.Write("['{0}_{1}',", poi.Location.X, poi.Location.Z);
533+ jsonWriter.Write("{");
534+ jsonWriter.Write("prettyCoord:'{0}',", poi.Location.PrettyCoords(ClientAPI));
535+ jsonWriter.Write("notes:'{0}',", poi.Notes.Replace("'", "\'").Replace("\n","\\n"));
536+ jsonWriter.Write("time:new Date('{0}'),", poi.Timestamp.ToString("O"));
537+ jsonWriter.Write("chunkPos:'{0}_{1}',", (poi.Location.X / chunkSize), (poi.Location.Z / chunkSize));
538+ jsonWriter.Write("}],");
539+ }
540+
541+ foreach (var poi in EOIs.PointsList)
542+ {
543+ jsonWriter.Write("['{0}_{1}',", poi.Location.X, poi.Location.Z);
544+ jsonWriter.Write("{");
545+ jsonWriter.Write("prettyCoord:'{0}',", poi.Location.PrettyCoords(ClientAPI));
546+ jsonWriter.Write("notes:'{0}',", poi.Notes.Replace("'", "\'"));
547+ jsonWriter.Write("time:new Date('{0}'),", poi.Timestamp.ToString("O"));
548+ jsonWriter.Write("chunkPos:'{0}_{1}',", (poi.Location.X / chunkSize), (poi.Location.Z / chunkSize));
549+ jsonWriter.Write("}],");
550+ }
551+ jsonWriter.Write("]);");
552+
553+ jsonWriter.Flush();
554+ }
555+
556+ }
557+
558+
559+ private ColumnMeta CreateColumnMetadata(KeyValuePair<Vec2i, uint> mostActiveCol, IMapChunk mapChunk)
560+ {
561+ ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), chunkSize);
562+ BlockPos equivBP = new BlockPos(mostActiveCol.Key.X * chunkSize,
563+ mapChunk.YMax,
564+ mostActiveCol.Key.Y * chunkSize);
565+
566+ var climate = ClientAPI.World.BlockAccessor.GetClimateAt(equivBP);
567+ data.UpdateFieldsFrom(climate, mapChunk, TimeSpan.FromHours(ClientAPI.World.Calendar.TotalHours));
568+
569+ return data;
570+ }
571+
572+ /// <summary>
573+ /// Reload chunk bounds from chunk shards
574+ /// </summary>
575+ /// <returns>The metadata.</returns>
576+ private void Reload_Metadata()
577+ {
578+ var worldmapDir = new DirectoryInfo(path);
579+
580+ if (worldmapDir.Exists)
581+ {
582+
583+ var files = worldmapDir.GetFiles(chunkFile_filter);
584+
585+ if (files.Length > 0)
586+ {
587+#if DEBUG
588+ Logger.VerboseDebug("{0} Existing world chunk shards", files.Length);
589+#endif
132590
133- try
134- {
135- uint ejectedItem = 0;
136- uint updatedChunks = 0;
137591
138- //-- Should dodge enumerator changing underfoot....at a cost.
139- if (!columnCounter.IsEmpty)
140- {
141- var tempSet = columnCounter.ToArray().OrderByDescending(kvp => kvp.Value);
142- foreach (var mostActiveCol in tempSet)
143- {
144592
145- var mapChunk = ClientAPI.World.BlockAccessor.GetMapChunk(mostActiveCol.Key);
593+ foreach (var shardFile in files)
594+ {
146595
147- if (mapChunk == null)
148- {
149- Logger.Warning("SKIP CHUNK: ({0}) - Map Chunk NULL!", mostActiveCol.Key);
150- nullChunkCount++;
151- columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
152- continue;
153- }
596+ if (shardFile.Length < 512) continue;
597+ var result = chunkShardRegex.Match(shardFile.Name);
598+ if (result.Success)
599+ {
600+ int X_chunk_pos = int.Parse(result.Groups["X"].Value);
601+ int Z_chunk_pos = int.Parse(result.Groups["Z"].Value);
154602
155- ColumnMeta chunkMeta = CreateColumnMetadata(mostActiveCol, mapChunk);
156- PngWriter pngWriter = SetupPngImage(mostActiveCol.Key, chunkMeta);
157- UpdateEntityMetadata();
158- ProcessChunkBlocks(mostActiveCol.Key, mapChunk, chunkMeta);
603+ //Parse PNG chunks for METADATA in shard
604+ using (var fileStream = shardFile.OpenRead())
605+ {
606+ //TODO: Add corrupted PNG Exception handing HERE !
607+ PngReader pngRead = new PngReader(fileStream);
608+ pngRead.ReadSkippingAllRows();
609+ pngRead.End();
159610
611+ PngMetadataChunk metadataFromPng = pngRead.GetChunksList().GetById1(PngMetadataChunk.ID) as PngMetadataChunk;
160612
161- ChunkRenderer.GenerateChunkPngShard(mostActiveCol.Key, mapChunk, chunkMeta, pngWriter, out uint updatedPixels);
613+ chunkTopMetadata.Add(metadataFromPng.ChunkMetadata);
614+ }
162615
163- if (updatedPixels > 0)
164- {
616+ }
617+ }
165618
619+ }
620+ }
621+ else
622+ {
166623 #if DEBUG
167- Logger.VerboseDebug("Wrote chunk shard: ({0}) - Edits#:{1}, Pixels#:{2}", mostActiveCol.Key, mostActiveCol.Value, updatedPixels);
624+ Logger.VerboseDebug("Could not open world map directory");
168625 #endif
169- updatedChunks++;
170- chunkTopMetadata.Update(chunkMeta);
171- columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
172- }
173- else
174- {
175- columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
176- Logger.VerboseDebug("Un-painted chunk: ({0}) ", mostActiveCol.Key);
177- }
178-
179- }
180- }
181-
182- if (updatedChunks > 0)
183- {
184- //What about chunk updates themselves; a update bitmap isn't kept...
185- updatedChunksTotal += updatedChunks;
186- GenerateMapHTML();
187- updatedChunks = 0;
188- }
189-
190- //Then sleep until interupted again, and repeat
191-
192- Logger.VerboseDebug("Thread '{0}' about to sleep indefinitely.", Thread.CurrentThread.Name);
193-
194- Thread.Sleep(Timeout.Infinite);
195-
196- }
197- catch (ThreadInterruptedException)
198- {
199-
200- Logger.VerboseDebug("Thread '{0}' interupted [awoken]", Thread.CurrentThread.Name);
201- goto wake;
202-
203- }
204- catch (ThreadAbortException)
205- {
206- Logger.VerboseDebug("Thread '{0}' aborted.", Thread.CurrentThread.Name);
207-
208- }
209- finally
210- {
211- Logger.VerboseDebug("Thread '{0}' executing finally block.", Thread.CurrentThread.Name);
212- }
213- }
214-
215- private void Prefill_POI_Designators()
216- {
217- this.POIs = new PointsOfInterest();
218- this.EOIs = new EntitiesOfInterest();
219- this.BlockID_Designators = new Dictionary<int, BlockDesignator>();
220- this.Entity_Designators = new Dictionary<AssetLocation, EntityDesignator>();
221-
222- //Add special marker types for BlockID's of "Interest", overwrite colour, and method
223-
224- Install_POI_Designators(DefaultDesignators.DefaultBlockDesignators(), DefaultDesignators.DefaultEntityDesignators());
225- }
226-
227- private void Install_POI_Designators(ICollection<BlockDesignator> blockDesig, List<EntityDesignator> entDesig)
228- {
229- Logger.VerboseDebug("Connecting {0} standard Block-Designators", blockDesig.Count);
230- foreach (var designator in blockDesig)
231- {
232- var blockIDs = Helpers.ArbitrarytBlockIdHunter(ClientAPI, designator.Pattern, designator.Material);
233- if (blockIDs.Count > 0) { Logger.VerboseDebug("Designator {0} has {1} associated blockIDs", designator.ToString(), blockIDs.Count); }
234- foreach (var entry in blockIDs)
235- {
236- BlockID_Designators.Add(entry.Key, designator);
237- }
238- }
239- this.ChunkRenderer.BlockID_Designators = BlockID_Designators;
240-
241-
242- Logger.VerboseDebug("Connecting {0} standard Entity-Designators", entDesig.Count);
243- foreach (var designator in entDesig)
244- {
245- //Get Variants first, from EntityTypes...better be populated!
246- var matched = ClientAPI.World.EntityTypes.FindAll(entp => entp.Code.BeginsWith(designator.Pattern.Domain, designator.Pattern.Path));
247-
248- foreach (var match in matched)
249- {
250- Logger.VerboseDebug("Linked Entity: {0} Designator: {1}", match.Code, designator);
251- this.Entity_Designators.Add(match.Code, designator);
252- }
253-
254-
255-
256- //EntityProperties props = ClientAPI.World.GetEntityType(designator.Pattern);
257- }
258-
259-
260- }
261-
262- //TODO: Convert to RAZOR model
263- private void GenerateMapHTML()
264- {
265- string mapFilename = Path.Combine(path, "Automap.html");
266-
267- int TopNorth = chunkTopMetadata.North_mostChunk;
268- int TopSouth = chunkTopMetadata.South_mostChunk;
269- int TopEast = chunkTopMetadata.East_mostChunk;
270- int TopWest = chunkTopMetadata.West_mostChunk;
271-
272- using (StreamWriter outputText = new StreamWriter(File.Open(mapFilename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)))
273- {
274- using (HtmlTextWriter tableWriter = new HtmlTextWriter(outputText))
275- {
276- tableWriter.BeginRender();
277- tableWriter.RenderBeginTag(HtmlTextWriterTag.Html);
278-
279- tableWriter.RenderBeginTag(HtmlTextWriterTag.Head);
280- tableWriter.RenderBeginTag(HtmlTextWriterTag.Title);
281- tableWriter.WriteEncodedText("Generated Automap");
282- tableWriter.RenderEndTag();
283- //CSS style here
284- tableWriter.RenderBeginTag(HtmlTextWriterTag.Style);
285- tableWriter.Write(stylesFile.ToText());
286- tableWriter.RenderEndTag();//</style>
287-
288- //## JSON map-state data ######################
289- tableWriter.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
290- tableWriter.RenderBeginTag(HtmlTextWriterTag.Script);
291-
292- tableWriter.Write("var available_images = [");
293-
294- foreach (var shard in this.chunkTopMetadata)
295- {
296- tableWriter.Write("{{X:{0},Y:{1} }}, ", shard.Location.X, shard.Location.Y);
297- }
298-
299- tableWriter.Write(" ];\n");
300-
301- tableWriter.RenderEndTag();
302-
303- tableWriter.RenderEndTag();
304-
305- tableWriter.RenderBeginTag(HtmlTextWriterTag.Body);
306- tableWriter.RenderBeginTag(HtmlTextWriterTag.P);
307- tableWriter.WriteEncodedText($"Created {DateTimeOffset.UtcNow.ToString("u")}");
308- tableWriter.RenderEndTag();
309- tableWriter.RenderBeginTag(HtmlTextWriterTag.P);
310- tableWriter.WriteEncodedText($"W:{TopWest}, E: {TopEast}, N:{TopNorth}, S:{TopSouth} ");
311- tableWriter.RenderEndTag();
312- tableWriter.WriteLine();
313- tableWriter.RenderBeginTag(HtmlTextWriterTag.Table);
314- tableWriter.RenderBeginTag(HtmlTextWriterTag.Caption);
315- tableWriter.WriteEncodedText($"Start: {startChunkColumn}, Seed: {ClientAPI.World.Seed}\n");
316- tableWriter.RenderEndTag();
317-
318- //################ X-Axis <thead> #######################
319- tableWriter.RenderBeginTag(HtmlTextWriterTag.Thead);
320- tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr);
321-
322- tableWriter.RenderBeginTag(HtmlTextWriterTag.Th);
323- tableWriter.Write("N, W");
324- tableWriter.RenderEndTag();
325-
326- for (int xAxisT = TopWest; xAxisT <= TopEast; xAxisT++)
327- {
328- tableWriter.RenderBeginTag(HtmlTextWriterTag.Th);
329- tableWriter.Write(xAxisT);
330- tableWriter.RenderEndTag();
331- }
332-
333- tableWriter.RenderBeginTag(HtmlTextWriterTag.Th);
334- tableWriter.Write("N, E");
335- tableWriter.RenderEndTag();
336-
337- tableWriter.RenderEndTag();
338- tableWriter.RenderEndTag();
339- //###### </thead> ################################
340-
341- //###### <tbody> - Chunk rows & Y-axis cols
342- tableWriter.RenderBeginTag(HtmlTextWriterTag.Tbody);
343-
344- //######## <tr> for every vertical row
345- for (int yAxis = TopNorth; yAxis <= TopSouth; yAxis++)
346- {
347- tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr);
348- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
349- tableWriter.Write(yAxis);//legend: Y-axis
350- tableWriter.RenderEndTag();
351-
352- for (int xAxis = TopWest; xAxis <= TopEast; xAxis++)
353- {
354- //###### <td> #### for chunk shard
355- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
356- var colLoc = new Vec2i(xAxis, yAxis);
357- if (chunkTopMetadata.Contains(colLoc))
358- {
359- ColumnMeta meta = chunkTopMetadata[colLoc];
360- //Tooltip first
361- tableWriter.AddAttribute(HtmlTextWriterAttribute.Class, "tooltip");
362- tableWriter.RenderBeginTag(HtmlTextWriterTag.Div);
363-
364- tableWriter.AddAttribute(HtmlTextWriterAttribute.Src, $"{xAxis}_{yAxis}.png");
365- tableWriter.RenderBeginTag(HtmlTextWriterTag.Img);
366- tableWriter.RenderEndTag();
367- // <span class="tooltiptext">Tooltip text
368- tableWriter.AddAttribute(HtmlTextWriterAttribute.Class, "tooltiptext");
369- tableWriter.RenderBeginTag(HtmlTextWriterTag.Span);
370-
371- StringBuilder tooltipText = new StringBuilder();
372- tooltipText.Append($"{meta.Location.PrettyCoords(ClientAPI)} ");
373- tooltipText.Append($" Max-Height: {meta.YMax}, Temp: {meta.Temperature.ToString("F1")} ");
374- tooltipText.Append($" Rainfall: {meta.Rainfall.ToString("F1")}, ");
375- tooltipText.Append($" Shrubs: {meta.ShrubDensity.ToString("F1")}, ");
376- tooltipText.Append($" Forest: {meta.ForestDensity.ToString("F1")}, ");
377- tooltipText.Append($" Fertility: {meta.Fertility.ToString("F1")}, ");
378-
379- if (meta.RockRatio != null)
380- {
381- foreach (KeyValuePair<int, uint> blockID in meta.RockRatio)
382- {
383- var block = ClientAPI.World.GetBlock(blockID.Key);
384- tooltipText.AppendFormat(" {0} × {1},\t", block.Code.GetName(), meta.RockRatio[blockID.Key]);
385- }
386- }
387-
388- tableWriter.WriteEncodedText(tooltipText.ToString());
389-
390- tableWriter.RenderEndTag();//</span>
391-
392-
393- tableWriter.RenderEndTag();//</div> --tooltip enclosure
394- }
395- else
396- {
397- tableWriter.Write("?");
398- }
399-
400- tableWriter.RenderEndTag();
401- }//############ </td> ###########
402-
403- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
404- tableWriter.Write(yAxis);//legend: Y-axis
405- tableWriter.RenderEndTag();
406-
407- tableWriter.RenderEndTag();
408-
409- }
410- tableWriter.RenderEndTag();
411-
412- //################ X-Axis <tfoot> #######################
413- tableWriter.RenderBeginTag(HtmlTextWriterTag.Tfoot);
414- tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr);
415-
416- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
417- tableWriter.Write("S, W");
418- tableWriter.RenderEndTag();
419-
420- for (int xAxisB = TopWest; xAxisB <= TopEast; xAxisB++)
421- {
422- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
423- tableWriter.Write(xAxisB);
424- tableWriter.RenderEndTag();
425- }
426-
427- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
428- tableWriter.Write("S, E");
429- tableWriter.RenderEndTag();
430-
431- tableWriter.RenderEndTag();
432- tableWriter.RenderEndTag();
433- //###### </tfoot> ################################
434-
435-
436- tableWriter.RenderEndTag();//</table>
437-
438- //############## POI list #####################
439- tableWriter.RenderBeginTag(HtmlTextWriterTag.P);
440- tableWriter.WriteLine("Points of Interest");
441- tableWriter.RenderEndTag();
442- tableWriter.RenderBeginTag(HtmlTextWriterTag.Ul);
443- foreach (var poi in this.POIs)
444- {
445- tableWriter.RenderBeginTag(HtmlTextWriterTag.Li);
446- tableWriter.WriteEncodedText(poi.Location.PrettyCoords(this.ClientAPI) + "\t");
447- tableWriter.WriteEncodedText(poi.Notes + "\t");
448- tableWriter.WriteEncodedText(poi.Timestamp.ToString("u"));
449- tableWriter.RenderEndTag();
450- }
451-
452- foreach (var eoi in this.EOIs.PointsList)
453- {
454- tableWriter.RenderBeginTag(HtmlTextWriterTag.Li);
455- tableWriter.WriteEncodedText(eoi.Location.PrettyCoords(this.ClientAPI) + "\t");
456- tableWriter.WriteEncodedText(eoi.Notes + "\t");
457- tableWriter.WriteEncodedText(eoi.Timestamp.ToString("u"));
458- tableWriter.RenderEndTag();
459- }
626+ }
460627
461- tableWriter.RenderEndTag();
462628
463629
630+ }
464631
632+ private PngWriter SetupPngImage(Vec2i coord, ColumnMeta metadata)
633+ {
634+ ImageInfo imageInf = new ImageInfo(chunkSize, chunkSize, 8, false);
465635
466- tableWriter.RenderEndTag();//### </BODY> ###
636+ string filename = $"{coord.X}_{coord.Y}.png";
637+ filename = Path.Combine(path, filename);
467638
468- tableWriter.EndRender();
469- tableWriter.Flush();
470- }
471- outputText.Flush();
472- }
639+ PngWriter pngWriter = FileHelper.CreatePngWriter(filename, imageInf, true);
640+ PngMetadata meta = pngWriter.GetMetadata();
641+ meta.SetTimeNow();
642+ meta.SetText("Chunk_X", coord.X.ToString("D"));
643+ meta.SetText("Chunk_Y", coord.Y.ToString("D"));
644+ //Setup specialized meta-data PNG chunks here...
645+ PngMetadataChunk pngChunkMeta = new PngMetadataChunk(pngWriter.ImgInfo);
646+ pngChunkMeta.ChunkMetadata = metadata;
647+ pngWriter.GetChunksList().Queue(pngChunkMeta);
473648
474- Logger.VerboseDebug("Generated HTML map");
475- }
649+ return pngWriter;
650+ }
476651
652+ /// <summary>
653+ /// Does the heavy lifting of Scanning columns of chunks - creates Heightmap and Processes POIs, Entities, and stats...
654+ /// </summary>
655+ /// <param name="key">Chunk Coordinate</param>
656+ /// <param name="mapChunk">Map chunk.</param>
657+ /// <param name="chunkMeta">Chunk metadata</param>
658+ private void ProcessChunkBlocks(Vec2i key, IMapChunk mapChunk, ColumnMeta chunkMeta)
659+ {
477660
661+ int targetChunkY = mapChunk.YMax / chunkSize;//Surface ...
662+ for (; targetChunkY > 0; targetChunkY--)
663+ {
664+ WorldChunk chunkData = ClientAPI.World.BlockAccessor.GetChunk(key.X, targetChunkY, key.Y) as WorldChunk;
478665
479- private ColumnMeta CreateColumnMetadata(KeyValuePair<Vec2i, uint> mostActiveCol, IMapChunk mapChunk)
480- {
481- ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), chunkSize);
482- BlockPos equivBP = new BlockPos(mostActiveCol.Key.X * chunkSize,
483- mapChunk.YMax,
484- mostActiveCol.Key.Y * chunkSize);
666+ if (chunkData == null || chunkData.BlockEntities == null)
667+ {
668+#if DEBUG
669+ Logger.VerboseDebug("Chunk null or empty X{0} Y{1} Z{2}", key.X, targetChunkY, key.Y);
670+#endif
671+ continue;
672+ }
485673
486- var climate = ClientAPI.World.BlockAccessor.GetClimateAt(equivBP);
487- data.UpdateFieldsFrom(climate, mapChunk, TimeSpan.FromHours(ClientAPI.World.Calendar.TotalHours));
674+ /*************** Chunk Entities Scanning *********************/
675+ if (chunkData.BlockEntities != null && chunkData.BlockEntities.Length > 0)
676+ {
677+#if DEBUG
678+ Logger.VerboseDebug("Surface@ {0} = BlockEntities: {1}", key, chunkData.BlockEntities.Length);
679+#endif
488680
489- return data;
490- }
681+ foreach (var blockEnt in chunkData.BlockEntities)
682+ {
683+
684+ if (blockEnt != null && blockEnt.Block != null && BlockID_Designators.ContainsKey(blockEnt.Block.BlockId))
685+ {
686+ var designator = BlockID_Designators[blockEnt.Block.BlockId];
687+ designator.SpecialAction(ClientAPI, POIs, blockEnt.Pos.Copy(), blockEnt.Block);
688+ }
689+ }
690+
691+ }
692+ /********************* Chunk/Column BLOCKs scanning ****************/
693+ //Heightmap, Stats, block tally
694+ chunkData.Unpack();
695+
696+ int X_index, Y_index, Z_index;
697+ X_index = Y_index = Z_index = 0;
698+
699+ do
700+ {
701+ do
702+ {
703+ do
704+ {
705+ /* Encode packed indicie
706+ (y * chunksize + z) * chunksize + x
707+ */
708+ var indicie = Helpers.ChunkBlockIndicie16(X_index, Y_index, Z_index);
709+ int aBlockId = chunkData.Blocks[indicie];
710+
711+ if (aBlockId == 0)
712+ {//Air
713+ chunkMeta.AirBlocks++;
714+ continue;
715+ }
716+
717+ if (RockIdCodes.ContainsKey(aBlockId))
718+ {
719+ if (chunkMeta.RockRatio.ContainsKey(aBlockId)) { chunkMeta.RockRatio[aBlockId]++; } else { chunkMeta.RockRatio.Add(aBlockId, 1); }
720+ }
721+
722+ chunkMeta.NonAirBlocks++;
723+
724+ //Heightmap
725+ if (chunkMeta.HeightMap[X_index, Z_index] == 0)
726+ { chunkMeta.HeightMap[X_index, Z_index] = (ushort) (Y_index + (targetChunkY * chunkSize)); }
727+
728+ }
729+ while (X_index++ < (chunkSize - 1));
730+ X_index = 0;
731+ }
732+ while (Z_index++ < (chunkSize - 1));
733+ Z_index = 0;
734+ }
735+ while (Y_index++ < (chunkSize - 1));
736+
737+ }
738+ }
739+
740+ private void UpdateEntityMetadata()
741+ {
742+ Logger.Debug("Presently {0} Entities", ClientAPI.World.LoadedEntities.Count);
743+ //Mabey scan only for 'new' entities by tracking ID in set?
744+ foreach (var loadedEntity in ClientAPI.World.LoadedEntities.ToList())
745+ {
491746
492- /// <summary>
493- /// Reload chunk bounds from chunk shards
494- /// </summary>
495- /// <returns>The metadata.</returns>
496- private void Reload_Metadata()
497- {
498- var worldmapDir = new DirectoryInfo(path);
747+#if DEBUG
748+ //Logger.VerboseDebug($"ENTITY: ({loadedEntity.Value.Code}) = #{loadedEntity.Value.EntityId} {loadedEntity.Value.State} {loadedEntity.Value.LocalPos} <<<<<<<<<<<<");
749+#endif
499750
500- if (worldmapDir.Exists)
501- {
751+ var dMatch = Entity_Designators.SingleOrDefault(se => se.Key.Equals(loadedEntity.Value.Code));
752+ if (dMatch.Value != null)
753+ {
754+ dMatch.Value.SpecialAction(ClientAPI, this.EOIs, loadedEntity.Value.LocalPos.AsBlockPos.Copy(), loadedEntity.Value);
755+ }
502756
503- var files = worldmapDir.GetFiles(chunkFile_filter);
757+ }
504758
505- if (files.Length > 0)
506- {
507-#if DEBUG
508- Logger.VerboseDebug("{0} Existing world chunk shards", files.Length);
509-#endif
510- foreach (var shardFile in files)
511- {
512759
513- if (shardFile.Length < 1000) continue;
514- var result = chunkShardRegex.Match(shardFile.Name);
515- if (result.Success)
516- {
517- int X_chunk_pos = int.Parse(result.Groups["X"].Value);
518- int Z_chunk_pos = int.Parse(result.Groups["Z"].Value);
760+ }
519761
520- //Parse PNG chunks for METADATA in shard
521- using var fileStream = shardFile.OpenRead();
522- PngReader pngRead = new PngReader(fileStream);
523- pngRead.ReadSkippingAllRows();
524- pngRead.End();
762+ private void AddNote(string notation)
763+ {
764+ var playerNodePoi = new PointOfInterest()
765+ {
766+ Location = ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Copy(),
767+ Notes = notation,
768+ Timestamp = DateTimeOffset.UtcNow,
769+ };
525770
526- PngMetadataChunk metadataFromPng = pngRead.GetChunksList().GetById1(PngMetadataChunk.ID) as PngMetadataChunk;
771+ this.POIs.AddReplace(playerNodePoi);
772+ }
527773
528- chunkTopMetadata.Add(metadataFromPng.ChunkMetadata);
529774
530- }
531- }
532775
533- }
534- }
535- else
536- {
537-#if DEBUG
538- Logger.VerboseDebug("Could not open world map directory");
539-#endif
540- }
541-
542-
543-
544- }
545-
546- private PngWriter SetupPngImage(Vec2i coord, ColumnMeta metadata)
547- {
548- ImageInfo imageInf = new ImageInfo(chunkSize, chunkSize, 8, false);
549-
550- string filename = $"{coord.X}_{coord.Y}.png";
551- filename = Path.Combine(path, filename);
552-
553- PngWriter pngWriter = FileHelper.CreatePngWriter(filename, imageInf, true);
554- PngMetadata meta = pngWriter.GetMetadata();
555- meta.SetTimeNow();
556- meta.SetText("Chunk_X", coord.X.ToString("D"));
557- meta.SetText("Chunk_Y", coord.Y.ToString("D"));
558- //Setup specialized meta-data PNG chunks here...
559- PngMetadataChunk pngChunkMeta = new PngMetadataChunk(pngWriter.ImgInfo)
560- {
561- ChunkMetadata = metadata
562- };
563- pngWriter.GetChunksList().Queue(pngChunkMeta);
564-
565- return pngWriter;
566- }
567-
568- /// <summary>
569- /// Does the heavy lifting of Scanning columns of chunks - creates Heightmap and Processes POIs, Entities, and stats...
570- /// </summary>
571- /// <param name="key">Chunk Coordinate</param>
572- /// <param name="mapChunk">Map chunk.</param>
573- /// <param name="chunkMeta">Chunk metadata</param>
574- private void ProcessChunkBlocks(Vec2i key, IMapChunk mapChunk, ColumnMeta chunkMeta)
575- {
576- //TODO: build stack of chunk(s) - surface down to bedrock
577- int topChunkY = mapChunk.YMax / chunkSize;
578- WorldChunk chunkData = (Vintagestory.Common.WorldChunk)ClientAPI.World.BlockAccessor.GetChunk(key.X, topChunkY, key.Y);
579-
580- if (chunkData.BlockEntities != null && chunkData.BlockEntities.Length > 0)
581- {
582-#if DEBUG
583- Logger.VerboseDebug("Surface@ {0} = BlockEntities: {1}", key, chunkData.BlockEntities.Length);
584-
585- foreach (var blockEnt in chunkData.BlockEntities)
586- {
587- if (BlockID_Designators.ContainsKey(blockEnt.Block.BlockId))
588- {
589- var designator = BlockID_Designators[blockEnt.Block.BlockId];
590- designator.SpecialAction(ClientAPI, POIs, blockEnt.Pos.Copy(), blockEnt.Block);
591- }
592- }
593-#endif
594- }
776+ private void CommandListener(string eventName, ref EnumHandling handling, IAttribute data)
777+ {
778+ Logger.VerboseDebug("MsgBus RX: AutomapCommandMsg: {0}", data.ToJsonToken());
595779
596- }
780+ CommandData cmdData = data as CommandData;
597781
598- private void UpdateEntityMetadata()
599- {
600- Logger.Debug("Presently {0} Entities", ClientAPI.World.LoadedEntities.Count);
601- //Mabey scan only for 'new' entities by tracking ID in set?
602- foreach (var loadedEntity in ClientAPI.World.LoadedEntities.ToList())
603- {
604782
605-#if DEBUG
606- //Logger.VerboseDebug($"ENTITY: ({loadedEntity.Value.Code}) = #{loadedEntity.Value.EntityId} {loadedEntity.Value.State} {loadedEntity.Value.LocalPos} <<<<<<<<<<<<");
607-#endif
783+ if (CurrentState != RunState.Snapshot)
784+ {
785+ switch (cmdData.State)
786+ {
787+ case RunState.Run:
788+ CurrentState = cmdData.State;
789+ AwakenCartographer(0.0f);
790+ break;
608791
609- var dMatch = Entity_Designators.SingleOrDefault(se => se.Key.Equals(loadedEntity.Value.Code));
610- if (dMatch.Value != null)
611- {
612- if (dMatch.Value.Enabled)
613- {
614- dMatch.Value.SpecialAction(ClientAPI, this.EOIs, loadedEntity.Value.LocalPos.AsBlockPos.Copy(), loadedEntity.Value);
615- }
616- }
792+ case RunState.Stop:
793+ CurrentState = cmdData.State;
794+ break;
617795
618- }
796+ case RunState.Snapshot:
797+ CurrentState = RunState.Stop;
798+ //Snapshot starts a second thread/process...
619799
800+ break;
620801
621- }
802+ case RunState.Notation:
803+ //Add to POI list where player location
804+ AddNote(cmdData.Notation);
805+ break;
806+ }
622807
808+ }
623809
810+ if (CurrentState != cmdData.State)
811+ {
812+ CurrentState = cmdData.State;
813+ AwakenCartographer(0.0f);
814+ }
624815
816+#if DEBUG
817+ ClientAPI.TriggerChatMessage($"Automap commanded to: {cmdData.State} ");
818+#endif
625819
820+ }
626821
627- #endregion
628822
823+ #endregion
629824
630- }
825+ }
631826
632827 }
\ No newline at end of file
--- a/Automap/modinfo.json
+++ b/Automap/modinfo.json
@@ -7,7 +7,7 @@
77 "version": "0.1.2",
88 "side":"Client",
99 "dependencies": {
10- "game": "1.11.0"
10+ "game": "1.12.0"
1111 },
1212 "website": "http://nowebsite.nope"
1313 }
\ No newline at end of file