-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using System.Threading;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.Linq;
-using System.Collections.Concurrent;
-
+
using Vintagestory.API.Client;
using Vintagestory.API.Common;
-using Vintagestory.API.Common.Entities;
-using Vintagestory.API.Config;
-using Vintagestory.API.MathTools;
-using Vintagestory.API.Server;
-using Vintagestory.GameContent;
-using Vintagestory.API.Datastructures;
namespace Automap
{
- public class AutomapMod : ModSystem
+ public partial class AutomapMod : ModSystem
{
private ICoreAPI API { get; set; }
private ICoreClientAPI ClientAPI { get; set; }
private ILogger Logger { get; set; }
- private Thread cartographer_thread;
-
- private const string _mapPath = @"Maps";
- private const string _chunkPath = @"Chunks";
- private ConcurrentDictionary<Vec2i, uint> columnCounter = new ConcurrentDictionary<Vec2i, uint>();
- private HashSet<Vec2i> knownChunkTops = new HashSet<Vec2i>();
- private Vec2i startPosition;
- private List<PointOfInterest> POIs;
- private Dictionary<int,Designator> BlockID_Designators;
public override bool ShouldLoad(EnumAppSide forSide)
{
- #region Internals
- private void StartAutomap( )
- {
- Prefill_POI_Designators( );
- startPosition = new Vec2i((ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.X / ClientAPI.World.BlockAccessor.ChunkSize), (ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Z/ ClientAPI.World.BlockAccessor.ChunkSize));
- Logger.Notification("AUTOMAP Start {0}", startPosition);
- ClientAPI.Event.ChunkDirty += ChunkAChanging;
-
- cartographer_thread = new Thread(Cartographer);
- cartographer_thread.Name = "Cartographer";
- cartographer_thread.Priority = ThreadPriority.Lowest;
- cartographer_thread.IsBackground = true;
-
- ClientAPI.Event.RegisterGameTickListener(AwakenCartographer, 6000);
- }
-
- private void AwakenCartographer(float delayed)
- {
-
- if (ClientAPI.IsGamePaused != false || ClientAPI.IsShuttingDown != true)
- {
- #if DEBUG
- Logger.VerboseDebug("Cartographer re-trigger from [{0}]", cartographer_thread.ThreadState);
- #endif
-
- if (cartographer_thread.ThreadState.HasFlag(ThreadState.Unstarted))
- {
- cartographer_thread.Start( );
- }
- else if (cartographer_thread.ThreadState.HasFlag(ThreadState.WaitSleepJoin)) {
- //Time to (re)write chunk shards
- cartographer_thread.Interrupt( );
- }
-
- ClientAPI.TriggerChatMessage($"Automap processed {knownChunkTops.Count} chunks.");
- }
-
- }
-
-
- private void Cartographer( )
- {
- wake:
- Logger.VerboseDebug("Cartographer thread awoken");
-
- try {
- uint ejectedItem = 0;
-
- while (columnCounter.Count > 0)
- {
- var mostActiveCol = columnCounter.OrderByDescending(kvp => kvp.Value).First();
- var mapChunk = ClientAPI.World.BlockAccessor.GetMapChunk(mostActiveCol.Key);
-
-
- if (mapChunk == null) {
- Logger.Warning("SKIP CHUNK: ({0}) - Map Chunk NULL!",mostActiveCol.Key);
-
- columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
- continue;
- }
-
- string filename = $"{mostActiveCol.Key.X}_{mostActiveCol.Key.Y}.png";
- string path = ClientAPI.GetOrCreateDataPath(_mapPath);
- path = ClientAPI.GetOrCreateDataPath(Path.Combine(path, "World_" + ClientAPI.World.Seed));
-
- filename = Path.Combine(path, filename);
-
- uint pixels = 0;
- var chkImg = GenerateChunkImage(mostActiveCol.Key, mapChunk, out pixels);
- if (pixels > 0) {
- chkImg.Save(filename, ImageFormat.Png);
- #if DEBUG
- Logger.VerboseDebug("Wrote chunk shard: ({0}) - Edits#:{1}, Pixels#:{2}", mostActiveCol.Key, mostActiveCol.Value, pixels);
- #endif
-
- knownChunkTops.Add(mostActiveCol.Key);
- columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
- }
- else {
- columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem);
- Logger.VerboseDebug("Un-painted chunk: ({0}) ", mostActiveCol.Key);
- }
-
- }
-
-
-
- //Then sleep until interupted again, and repeat
-
- Logger.VerboseDebug("Thread '{0}' about to sleep indefinitely.", Thread.CurrentThread.Name);
-
- Thread.Sleep(Timeout.Infinite);
-
- } catch (ThreadInterruptedException) {
-
- Logger.VerboseDebug("Thread '{0}' interupted [awoken]", Thread.CurrentThread.Name);
- goto wake;
-
- } catch (ThreadAbortException) {
- Logger.VerboseDebug("Thread '{0}' aborted.", Thread.CurrentThread.Name);
-
- } finally {
- Logger.VerboseDebug("Thread '{0}' executing finally block.", Thread.CurrentThread.Name);
- }
- }
- #endregion
-
-
-
- private void ChunkAChanging(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason)
- {
- //Logger.VerboseDebug($"Change: @({chunkCoord}) R: {reason}");
- Vec2i topPosition = new Vec2i(chunkCoord.X, chunkCoord.Z);
-
- columnCounter.AddOrUpdate(topPosition, 1, (key, colAct) => colAct + 1);
- }
-
- private void Prefill_POI_Designators( )
- {
- this.POIs = new List<PointOfInterest>( );
- var roadIDs = Helpers.ArbitrarytBlockIdHunter(ClientAPI, new AssetLocation("game","stonepath"), EnumBlockMaterial.Gravel);
-
- //Add special marker types for BlockID's of "Interest", plus a special overwrite colour for them
- this.BlockID_Designators = new Dictionary<int, Designator>( );
-
- foreach (var entry in roadIDs) {
- BlockID_Designators.Add (entry.Key, new Designator
- (
- Color.Yellow
- ));
- }
-
-
-
- }
-
-
-
- #region COPYPASTA
- //A slightly re-written; ChunkMapLayer :: public int[] GenerateChunkImage(Vec2i chunkPos, IMapChunk mc)
- internal Bitmap GenerateChunkImage(Vec2i chunkPos, IMapChunk mc, out uint pixelCount)
- {
- pixelCount = 0;
- BlockPos tmpPos = new BlockPos( );
- Vec2i localpos = new Vec2i( );
- int chunkSize = ClientAPI.World.BlockAccessor.ChunkSize;
- var chunksColumn = new IWorldChunk[ClientAPI.World.BlockAccessor.MapSizeY / chunkSize];
- Bitmap chunkImage = new Bitmap(chunkSize, chunkSize, PixelFormat.Format24bppRgb );
- int topChunkY = mc.YMax / chunkSize;//Heywaitaminute -- this isn't a highest FEATURE, if Rainmap isn't accurate!
- //Metadata of DateTime chunk was edited, chunk coords.,world-seed? Y-Max feature height
- //Grab a chunk COLUMN... Topmost Y down...
- for (int chunkY = 0; chunkY < topChunkY; chunkY++) {
- chunksColumn[chunkY] = ClientAPI.World.BlockAccessor.GetChunk(chunkPos.X, chunkY, chunkPos.Y);
- //What to do if chunk is a void? invalid?
- }
-
- // Prefetch map chunks, in pattern
- IMapChunk[ ] mapChunks = new IMapChunk[ ]
- {
- ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y - 1),
- ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y),
- ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X, chunkPos.Y - 1)
- };
-
-
- for (int posIndex = 0; posIndex < (chunkSize * chunkSize); posIndex++) {
- int mapY = mc.RainHeightMap[posIndex];
- int localChunkY = mapY / chunkSize;
- if (localChunkY >= (chunksColumn.Length ) ) continue;//Out of range!
-
- MapUtil.PosInt2d(posIndex, chunkSize, localpos);
- int localX = localpos.X;
- int localZ = localpos.Y;
-
- float b = 1;
- int leftTop, rightTop, leftBot;
-
- IMapChunk leftTopMapChunk = mc;
- IMapChunk rightTopMapChunk = mc;
- IMapChunk leftBotMapChunk = mc;
-
- int topX = localX - 1;
- int botX = localX;
- int leftZ = localZ - 1;
- int rightZ = localZ;
-
- if (topX < 0 && leftZ < 0) {
- leftTopMapChunk = mapChunks[0];
- rightTopMapChunk = mapChunks[1];
- leftBotMapChunk = mapChunks[2];
- }
- else {
- if (topX < 0) {
- leftTopMapChunk = mapChunks[1];
- rightTopMapChunk = mapChunks[1];
- }
- if (leftZ < 0) {
- leftTopMapChunk = mapChunks[2];
- leftBotMapChunk = mapChunks[2];
- }
- }
-
- topX = GameMath.Mod(topX, chunkSize);
- leftZ = GameMath.Mod(leftZ, chunkSize);
-
- leftTop = leftTopMapChunk == null ? 0 : Math.Sign(mapY - leftTopMapChunk.RainHeightMap[leftZ * chunkSize + topX]);
- rightTop = rightTopMapChunk == null ? 0 : Math.Sign(mapY - rightTopMapChunk.RainHeightMap[rightZ * chunkSize + topX]);
- leftBot = leftBotMapChunk == null ? 0 : Math.Sign(mapY - leftBotMapChunk.RainHeightMap[leftZ * chunkSize + botX]);
-
- float slopeness = (leftTop + rightTop + leftBot);
-
- if (slopeness > 0) b = 1.2f;
- if (slopeness < 0) b = 0.8f;
-
- b -= 0.15f; // Map seems overally a bit too bright
- //b = 1;
- if (chunksColumn[localChunkY] == null) {
-
- continue;
- }
-
- chunksColumn[localChunkY].Unpack( );
- int blockId = chunksColumn[localChunkY].Blocks[MapUtil.Index3d(localpos.X, mapY % chunkSize, localpos.Y, chunkSize, chunkSize)];
-
- Block block = ClientAPI.World.Blocks[blockId];
-
- tmpPos.Set(chunkSize * chunkPos.X + localpos.X, mapY, chunkSize * chunkPos.Y + localpos.Y);
-
- int avgCol = block.GetColor(ClientAPI, tmpPos);
- int rndCol = block.GetRandomColor(ClientAPI, tmpPos, BlockFacing.UP);
- //Merge color?
- int col = ColorUtil.ColorOverlay(avgCol, rndCol, 0.125f);
- var packedFormat = ColorUtil.ColorMultiply3Clamped(col, b) | 255 << 24;//Is the Struct, truly so undesirable?
-
- Color pixelColor = Color.FromArgb(ColorUtil.ColorR(packedFormat), ColorUtil.ColorG(packedFormat), ColorUtil.ColorB(packedFormat));
-
- //============ POI Population =================
- if (BlockID_Designators.ContainsKey(blockId)) {
- var desig = BlockID_Designators[blockId];
- pixelColor = desig.OverwriteColor;
-
- if (desig.SpecialAction != null) {
- desig.SpecialAction.Invoke(tmpPos, block);
- }
- }
-
- chunkImage.SetPixel(localX,localZ, pixelColor);
- pixelCount++;
- }
-
-
- return chunkImage;
- }
- #endregion
}
}