From d9642cc2ec5e7d70078132d237377cb9e0e0303f Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 02:40:54 +0800 Subject: [PATCH 01/24] Update Config.cs --- WorldMapper/Config.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WorldMapper/Config.cs b/WorldMapper/Config.cs index 06ec593..1a7bf9d 100644 --- a/WorldMapper/Config.cs +++ b/WorldMapper/Config.cs @@ -8,4 +8,4 @@ public class Config public bool SaveMapOnWorldSave { get; set; } public string MapFileNameFormat { get; set; } = "{0}_{1}_{2}.png"; } -} \ No newline at end of file +} From 81e57aacbd3df71a4c1cb77dedf735dfd3a3929a Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 02:41:45 +0800 Subject: [PATCH 02/24] Update DirectBitmap.cs --- WorldMapper/DirectBitmap.cs | 41 +++++++++++-------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/WorldMapper/DirectBitmap.cs b/WorldMapper/DirectBitmap.cs index 7280159..89c47fe 100644 --- a/WorldMapper/DirectBitmap.cs +++ b/WorldMapper/DirectBitmap.cs @@ -1,54 +1,37 @@ -// wooooo stealing code https://stackoverflow.com/questions/24701703/c-sharp-faster-alternatives-to-setpixel-and-getpixel-for-bitmaps-for-windows-f using System; -using System.Drawing; -using System.Drawing.Imaging; -using System.Runtime.InteropServices; +using System.IO; +using SkiaSharp; namespace WorldMapper { public class DirectBitmap : IDisposable { - public Bitmap Bitmap { get; private set; } - public Int32[] Bits { get; private set; } - public bool Disposed { get; private set; } + public SKBitmap Bitmap { get; private set; } public int Height { get; private set; } public int Width { get; private set; } - protected GCHandle BitsHandle { get; private set; } - public DirectBitmap(int width, int height) { Width = width; Height = height; - Bits = new Int32[width * height]; - BitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned); - Bitmap = new Bitmap(width, height, width * 4, PixelFormat.Format32bppPArgb, - BitsHandle.AddrOfPinnedObject()); + Bitmap = new SKBitmap(width, height, SKColorType.Rgba8888, SKAlphaType.Premul); } - public void SetPixel(int x, int y, Color colour) + public void SetPixel(int x, int y, SKColor colour) { - int index = x + (y * Width); - int col = colour.ToArgb(); - - Bits[index] = col; + Bitmap.SetPixel(x, y, colour); } - public Color GetPixel(int x, int y) + public void Save(string path) { - int index = x + (y * Width); - int col = Bits[index]; - Color result = Color.FromArgb(col); - - return result; + using var data = Bitmap.Encode(SKEncodedImageFormat.Png, 100); + using var stream = File.OpenWrite(path); + data.SaveTo(stream); } public void Dispose() { - if (Disposed) return; - Disposed = true; - Bitmap.Dispose(); - BitsHandle.Free(); + Bitmap?.Dispose(); } } -} \ No newline at end of file +} From 37d7de43905d4e23d48d49a78c8b8e0893f0cc8c Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 02:42:17 +0800 Subject: [PATCH 03/24] Update MapGenerator.cs --- WorldMapper/MapGenerator.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/WorldMapper/MapGenerator.cs b/WorldMapper/MapGenerator.cs index 38b83d6..c7541aa 100644 --- a/WorldMapper/MapGenerator.cs +++ b/WorldMapper/MapGenerator.cs @@ -1,4 +1,5 @@ using Microsoft.Xna.Framework; +using SkiaSharp; using Terraria; using Terraria.Map; @@ -8,9 +9,7 @@ public static class MapGenerator { public static DirectBitmap Create(Rectangle? region = null) { - Rectangle realRegion; - realRegion = region ?? new Rectangle(0, 0, Main.maxTilesX, Main.maxTilesY); - + Rectangle realRegion = region ?? new Rectangle(0, 0, Main.maxTilesX, Main.maxTilesY); var bitmap = new DirectBitmap(realRegion.Width, realRegion.Height); var replacedMap = false; @@ -29,7 +28,7 @@ public static DirectBitmap Create(Rectangle? region = null) var tile = MapHelper.CreateMapTile(x + realRegion.X, y + realRegion.Y, byte.MaxValue); var col = MapHelper.GetMapTileXnaColor(ref tile); - bitmap.SetPixel(x, y, System.Drawing.Color.FromArgb(col.A, col.R, col.G, col.B)); + bitmap.SetPixel(x, y, new SKColor(col.R, col.G, col.B, col.A)); } } @@ -38,4 +37,4 @@ public static DirectBitmap Create(Rectangle? region = null) return bitmap; } } -} \ No newline at end of file +} From a18faecffdb2385dda606b0f5fcf5e5009b9b10d Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 02:42:48 +0800 Subject: [PATCH 04/24] Update Plugin.cs --- WorldMapper/Plugin.cs | 50 +++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/WorldMapper/Plugin.cs b/WorldMapper/Plugin.cs index d474be4..8c5d211 100644 --- a/WorldMapper/Plugin.cs +++ b/WorldMapper/Plugin.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.IO; using System.Reflection; using Newtonsoft.Json; -using OTAPI; using Terraria; using TerrariaApi.Server; using TShockAPI; +using On.Terraria.IO; namespace WorldMapper { @@ -16,11 +16,10 @@ public class Plugin : TerrariaPlugin public override string Author => "James Puleo"; public override string Description => "Generates a PNG map of the entire world"; public override Version Version => Assembly.GetExecutingAssembly().GetName().Version; + private Config _config; - public Plugin(Main game) : base(game) - { - } + public Plugin(Main game) : base(game) { } public override void Initialize() { @@ -28,44 +27,53 @@ public override void Initialize() ? JsonConvert.DeserializeObject(File.ReadAllText(Config.DefaultPath)) : new Config(); - Hooks.World.IO.PostLoadWorld += OnPostLoadWorld; - Hooks.World.IO.PostSaveWorld += OnPostSaveWorld; + WorldFile.LoadWorld += OnLoadWorld; + WorldFile.SaveWorld += OnSaveWorld; Commands.ChatCommands.Add(new Command("worldmapper.generatemap", args => { var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); - var fileName = args.Parameters.Count >= 1 ? args.Parameters[0] : string.Format(_config.MapFileNameFormat, Main.worldName, "manual", now); using var bitmap = MapGenerator.Create(); - Save(bitmap, fileName); + bitmap.Save(fileName); + TShock.Log.ConsoleInfo($"Map generated and saved as {fileName}"); args.Player.SendSuccessMessage($"Map saved as {fileName}"); }, "generatemap")); } - private void OnPostSaveWorld(bool usecloudsaving, bool resettime) + protected override void Dispose(bool disposing) { - DoAutomaticGenerate("save"); + if (disposing) + { + WorldFile.LoadWorld -= OnLoadWorld; + WorldFile.SaveWorld -= OnSaveWorld; + } + base.Dispose(disposing); } - private void OnPostLoadWorld(bool loadfromcloud) + private void OnLoadWorld(On.Terraria.IO.WorldFile.orig_LoadWorld orig, bool loadFromCloud) { - DoAutomaticGenerate("load"); + orig(loadFromCloud); + if (_config.SaveMapOnWorldLoad) + DoAutomaticGenerate("load"); } - private void DoAutomaticGenerate(string why) + private void OnSaveWorld(On.Terraria.IO.WorldFile.orig_SaveWorld orig, bool useCloudSaving, bool resetTime) { - var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); - using var bitmap = MapGenerator.Create(); - Save(bitmap, string.Format(_config.MapFileNameFormat, Main.worldName, why, now)); + orig(useCloudSaving, resetTime); + if (_config.SaveMapOnWorldSave) + DoAutomaticGenerate("save"); } - private static void Save(DirectBitmap bitmap, string fileName) + private void DoAutomaticGenerate(string why) { - bitmap.Bitmap.Save(fileName); - TShock.Log.ConsoleInfo($"Map generated and saved as {fileName}"); + var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + using var bitmap = MapGenerator.Create(); + bitmap.Save(string.Format(_config.MapFileNameFormat, Main.worldName, why, now)); + TShock.Log.ConsoleInfo($"Map auto-generated ({why})"); } } -} \ No newline at end of file +} From 9775bf00255953305dcc94cf554e452722bbe693 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 02:43:24 +0800 Subject: [PATCH 05/24] Update WorldMapper.csproj --- WorldMapper/WorldMapper.csproj | 67 +++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/WorldMapper/WorldMapper.csproj b/WorldMapper/WorldMapper.csproj index 35e677e..2c2fc79 100644 --- a/WorldMapper/WorldMapper.csproj +++ b/WorldMapper/WorldMapper.csproj @@ -1,23 +1,54 @@ - - net48 - latest - + + net9.0 + WorldMapper + WorldMapper + 1.0.0.0 + latest + enable + true + - - - ..\..\tshock_test\Newtonsoft.Json.dll - - - ..\..\tshock_test\OTAPI.dll - - - ..\..\tshock_test\TerrariaServer.exe - - - ..\..\tshock_test\ServerPlugins\TShockAPI.dll - - + + + libs\OTAPI.dll + false + + + libs\TerrariaApi.Server.dll + false + + + libs\TSAPI.dll + false + + + libs\TerrariaServer.exe + false + + + libs\TerrariaServer.dll + false + + + libs\TShock.Server.dll + false + + + libs\TShockAPI.dll + false + + + libs\ModFramework.dll + false + + + + + + + + From 513c396fcbc01616c30f81e77f1c8c06c384e9e1 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 02:44:07 +0800 Subject: [PATCH 06/24] Create build.yml --- .github/workflows/build.yml | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ea89e35 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,41 @@ +name: Build WorldMapper + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' + + - name: Download TShock 6.1.0 + run: | + mkdir -p libs tshock_extract + URL="https://github.com/Pryaxis/TShock/releases/download/v6.1.0/TShock-6.1.0-for-Terraria-1.4.5.6-win-x64-Release.zip" + curl -L --fail -o tshock.zip "$URL" || curl -L --fail -o tshock.zip "https://github.com/Pryaxis/TShock/releases/download/v6.1.0/TShock-6.1.0-for-Terraria-1.4.5.6-linux-x64-Release.zip" + unzip -q tshock.zip -d tshock_extract || true + find tshock_extract -type f \( -iname "*.dll" -o -iname "*.exe" \) -exec cp {} libs/ \; + cd libs + [ -f TSAPI.dll ] && [ ! -f TerrariaApi.Server.dll ] && cp TSAPI.dll TerrariaApi.Server.dll + [ ! -f TerrariaServer.exe ] && [ -f TShock.Server.exe ] && cp TShock.Server.exe TerrariaServer.exe + [ ! -f TerrariaServer.dll ] && [ -f TShock.Server.dll ] && cp TShock.Server.dll TerrariaServer.dll + [ -f TShock.dll ] && [ ! -f TShockAPI.dll ] && cp TShock.dll TShockAPI.dll + ls -la + + - run: dotnet restore + + - run: dotnet build --configuration Release --no-restore + + - uses: actions/upload-artifact@v4 + with: + name: WorldMapper + path: bin/Release/net9.0/WorldMapper.dll From 787ed143ffc2625c2207613ec9ea8e20c8cbdd58 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 02:50:07 +0800 Subject: [PATCH 07/24] Update WorldMapper.csproj --- WorldMapper/WorldMapper.csproj | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/WorldMapper/WorldMapper.csproj b/WorldMapper/WorldMapper.csproj index 2c2fc79..c0593df 100644 --- a/WorldMapper/WorldMapper.csproj +++ b/WorldMapper/WorldMapper.csproj @@ -12,35 +12,31 @@ - libs\OTAPI.dll + ..\libs\OTAPI.dll false - - libs\TerrariaApi.Server.dll + + ..\libs\TerrariaApi.Server.dll false - - libs\TSAPI.dll + + ..\libs\TSAPI.dll false - - libs\TerrariaServer.exe + + ..\libs\TerrariaServer.exe false - - libs\TerrariaServer.dll + + ..\libs\TShock.Server.dll false - - libs\TShock.Server.dll + + ..\libs\TShockAPI.dll false - - libs\TShockAPI.dll - false - - - libs\ModFramework.dll + + ..\libs\ModFramework.dll false From 4c7f156b5b144c6344b2df9f0f80a6badbdde3de Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 02:50:40 +0800 Subject: [PATCH 08/24] Update build.yml --- .github/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ea89e35..b5b2a19 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,6 @@ jobs: cd libs [ -f TSAPI.dll ] && [ ! -f TerrariaApi.Server.dll ] && cp TSAPI.dll TerrariaApi.Server.dll [ ! -f TerrariaServer.exe ] && [ -f TShock.Server.exe ] && cp TShock.Server.exe TerrariaServer.exe - [ ! -f TerrariaServer.dll ] && [ -f TShock.Server.dll ] && cp TShock.Server.dll TerrariaServer.dll [ -f TShock.dll ] && [ ! -f TShockAPI.dll ] && cp TShock.dll TShockAPI.dll ls -la @@ -38,4 +37,4 @@ jobs: - uses: actions/upload-artifact@v4 with: name: WorldMapper - path: bin/Release/net9.0/WorldMapper.dll + path: WorldMapper/bin/Release/net9.0/WorldMapper.dll From a95c940197695478eae0c5a4c3986a2c3e14116a Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:33:25 +0800 Subject: [PATCH 09/24] Delete WorldMapper/WorldMapper.csproj --- WorldMapper/WorldMapper.csproj | 50 ---------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 WorldMapper/WorldMapper.csproj diff --git a/WorldMapper/WorldMapper.csproj b/WorldMapper/WorldMapper.csproj deleted file mode 100644 index c0593df..0000000 --- a/WorldMapper/WorldMapper.csproj +++ /dev/null @@ -1,50 +0,0 @@ - - - - net9.0 - WorldMapper - WorldMapper - 1.0.0.0 - latest - enable - true - - - - - ..\libs\OTAPI.dll - false - - - ..\libs\TerrariaApi.Server.dll - false - - - ..\libs\TSAPI.dll - false - - - ..\libs\TerrariaServer.exe - false - - - ..\libs\TShock.Server.dll - false - - - ..\libs\TShockAPI.dll - false - - - ..\libs\ModFramework.dll - false - - - - - - - - - - From 41bd6a5ee707d3da0ef6c57b52617618d184d2c5 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:34:02 +0800 Subject: [PATCH 10/24] Create WorldMapper.csproj --- WorldMapper.csproj | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 WorldMapper.csproj diff --git a/WorldMapper.csproj b/WorldMapper.csproj new file mode 100644 index 0000000..2c2fc79 --- /dev/null +++ b/WorldMapper.csproj @@ -0,0 +1,54 @@ + + + + net9.0 + WorldMapper + WorldMapper + 1.0.0.0 + latest + enable + true + + + + + libs\OTAPI.dll + false + + + libs\TerrariaApi.Server.dll + false + + + libs\TSAPI.dll + false + + + libs\TerrariaServer.exe + false + + + libs\TerrariaServer.dll + false + + + libs\TShock.Server.dll + false + + + libs\TShockAPI.dll + false + + + libs\ModFramework.dll + false + + + + + + + + + + From 8ae1365a109ac9f00a06adec32143976053a2dc5 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:35:16 +0800 Subject: [PATCH 11/24] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b5b2a19..9807f42 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,4 +37,4 @@ jobs: - uses: actions/upload-artifact@v4 with: name: WorldMapper - path: WorldMapper/bin/Release/net9.0/WorldMapper.dll + path: bin/Release/net9.0/WorldMapper.dll From 1b2ebf314a487b625ccd5559e7750027cf31618f Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:35:40 +0800 Subject: [PATCH 12/24] Update Plugin.cs --- WorldMapper/Plugin.cs | 53 +++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/WorldMapper/Plugin.cs b/WorldMapper/Plugin.cs index 8c5d211..dd695df 100644 --- a/WorldMapper/Plugin.cs +++ b/WorldMapper/Plugin.cs @@ -5,7 +5,6 @@ using Terraria; using TerrariaApi.Server; using TShockAPI; -using On.Terraria.IO; namespace WorldMapper { @@ -27,8 +26,7 @@ public override void Initialize() ? JsonConvert.DeserializeObject(File.ReadAllText(Config.DefaultPath)) : new Config(); - WorldFile.LoadWorld += OnLoadWorld; - WorldFile.SaveWorld += OnSaveWorld; + ServerApi.Hooks.GamePostInitialize.Register(this, OnPostInit); Commands.ChatCommands.Add(new Command("worldmapper.generatemap", args => { @@ -44,28 +42,42 @@ public override void Initialize() }, "generatemap")); } - protected override void Dispose(bool disposing) + private void OnPostInit(EventArgs args) { - if (disposing) - { - WorldFile.LoadWorld -= OnLoadWorld; - WorldFile.SaveWorld -= OnSaveWorld; - } - base.Dispose(disposing); + if (_config.SaveMapOnWorldLoad) + DoAutomaticGenerate("load"); + + if (_config.SaveMapOnWorldSave) + HookWorldSave(); } - private void OnLoadWorld(On.Terraria.IO.WorldFile.orig_LoadWorld orig, bool loadFromCloud) + private void HookWorldSave() { - orig(loadFromCloud); - if (_config.SaveMapOnWorldLoad) - DoAutomaticGenerate("load"); + try + { + var saveMethod = typeof(Terraria.IO.WorldFile).GetMethod("SaveWorld", new[] { typeof(bool), typeof(bool) }); + if (saveMethod == null) + { + TShock.Log.ConsoleError("[WorldMapper] Could not hook world save."); + return; + } + + var hook = new MonoMod.RuntimeDetour.Hook( + saveMethod, + typeof(Plugin).GetMethod(nameof(OnSaveWorld), BindingFlags.NonPublic | BindingFlags.Instance), + this + ); + } + catch (Exception ex) + { + TShock.Log.ConsoleError($"[WorldMapper] Save hook failed: {ex.Message}"); + } } - private void OnSaveWorld(On.Terraria.IO.WorldFile.orig_SaveWorld orig, bool useCloudSaving, bool resetTime) + private void OnSaveWorld(Action orig, bool useCloudSaving, bool resetTime) { orig(useCloudSaving, resetTime); - if (_config.SaveMapOnWorldSave) - DoAutomaticGenerate("save"); + DoAutomaticGenerate("save"); } private void DoAutomaticGenerate(string why) @@ -75,5 +87,12 @@ private void DoAutomaticGenerate(string why) bitmap.Save(string.Format(_config.MapFileNameFormat, Main.worldName, why, now)); TShock.Log.ConsoleInfo($"Map auto-generated ({why})"); } + + protected override void Dispose(bool disposing) + { + if (disposing) + ServerApi.Hooks.GamePostInitialize.Deregister(this, OnPostInit); + base.Dispose(disposing); + } } } From 0c4dc19eb8f8273ddadfbe4ae882e370273a3949 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:37:10 +0800 Subject: [PATCH 13/24] Update MapGenerator.cs From 9a204de55fa1ff42d7f953fffa383befb44eeb51 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:37:29 +0800 Subject: [PATCH 14/24] Update DirectBitmap.cs From 204d353cc4f0acda04ecdfdbc4afe42cbfd15f08 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:37:46 +0800 Subject: [PATCH 15/24] Update Config.cs From b054d908639c7de8f5189f7aec7955a01abde3a1 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 13:59:22 +0800 Subject: [PATCH 16/24] Delete WorldMapper.sln --- WorldMapper.sln | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 WorldMapper.sln diff --git a/WorldMapper.sln b/WorldMapper.sln deleted file mode 100644 index 504c355..0000000 --- a/WorldMapper.sln +++ /dev/null @@ -1,16 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorldMapper", "WorldMapper\WorldMapper.csproj", "{10880B32-0BD8-45B8-91F1-65A79DF520C9}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {10880B32-0BD8-45B8-91F1-65A79DF520C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {10880B32-0BD8-45B8-91F1-65A79DF520C9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {10880B32-0BD8-45B8-91F1-65A79DF520C9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {10880B32-0BD8-45B8-91F1-65A79DF520C9}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal From 6327d611c73552e99889724cdb990a1ad7819905 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 14:06:18 +0800 Subject: [PATCH 17/24] Update WorldMapper.csproj --- WorldMapper.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WorldMapper.csproj b/WorldMapper.csproj index 2c2fc79..ae32646 100644 --- a/WorldMapper.csproj +++ b/WorldMapper.csproj @@ -43,6 +43,10 @@ libs\ModFramework.dll false + + libs\MonoMod.RuntimeDetour.dll + false + From 67af62b654a44af4b17e58f376669c0db57e57d6 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 14:06:47 +0800 Subject: [PATCH 18/24] Update Plugin.cs --- WorldMapper/Plugin.cs | 61 +++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/WorldMapper/Plugin.cs b/WorldMapper/Plugin.cs index dd695df..05323a6 100644 --- a/WorldMapper/Plugin.cs +++ b/WorldMapper/Plugin.cs @@ -5,6 +5,7 @@ using Terraria; using TerrariaApi.Server; using TShockAPI; +using On.Terraria.IO; namespace WorldMapper { @@ -14,19 +15,20 @@ public class Plugin : TerrariaPlugin public override string Name => "World Mapper"; public override string Author => "James Puleo"; public override string Description => "Generates a PNG map of the entire world"; - public override Version Version => Assembly.GetExecutingAssembly().GetName().Version; + public override Version Version => Assembly.GetExecutingAssembly().GetName().Version ?? new Version(1, 0, 0, 0); - private Config _config; + private Config? _config; public Plugin(Main game) : base(game) { } public override void Initialize() { _config = File.Exists(Config.DefaultPath) - ? JsonConvert.DeserializeObject(File.ReadAllText(Config.DefaultPath)) + ? JsonConvert.DeserializeObject(File.ReadAllText(Config.DefaultPath)) ?? new Config() : new Config(); - ServerApi.Hooks.GamePostInitialize.Register(this, OnPostInit); + WorldFile.LoadWorld += OnLoadWorld; + WorldFile.SaveWorld += OnSaveWorld; Commands.ChatCommands.Add(new Command("worldmapper.generatemap", args => { @@ -42,57 +44,36 @@ public override void Initialize() }, "generatemap")); } - private void OnPostInit(EventArgs args) + protected override void Dispose(bool disposing) { - if (_config.SaveMapOnWorldLoad) - DoAutomaticGenerate("load"); - - if (_config.SaveMapOnWorldSave) - HookWorldSave(); + if (disposing) + { + WorldFile.LoadWorld -= OnLoadWorld; + WorldFile.SaveWorld -= OnSaveWorld; + } + base.Dispose(disposing); } - private void HookWorldSave() + private void OnLoadWorld(On.Terraria.IO.WorldFile.orig_LoadWorld orig, bool loadFromCloud) { - try - { - var saveMethod = typeof(Terraria.IO.WorldFile).GetMethod("SaveWorld", new[] { typeof(bool), typeof(bool) }); - if (saveMethod == null) - { - TShock.Log.ConsoleError("[WorldMapper] Could not hook world save."); - return; - } - - var hook = new MonoMod.RuntimeDetour.Hook( - saveMethod, - typeof(Plugin).GetMethod(nameof(OnSaveWorld), BindingFlags.NonPublic | BindingFlags.Instance), - this - ); - } - catch (Exception ex) - { - TShock.Log.ConsoleError($"[WorldMapper] Save hook failed: {ex.Message}"); - } + orig(loadFromCloud); + if (_config?.SaveMapOnWorldLoad == true) + DoAutomaticGenerate("load"); } - private void OnSaveWorld(Action orig, bool useCloudSaving, bool resetTime) + private void OnSaveWorld(On.Terraria.IO.WorldFile.orig_SaveWorld orig, bool useCloudSaving, bool resetTime) { orig(useCloudSaving, resetTime); - DoAutomaticGenerate("save"); + if (_config?.SaveMapOnWorldSave == true) + DoAutomaticGenerate("save"); } private void DoAutomaticGenerate(string why) { var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); using var bitmap = MapGenerator.Create(); - bitmap.Save(string.Format(_config.MapFileNameFormat, Main.worldName, why, now)); + bitmap.Save(string.Format(_config!.MapFileNameFormat, Main.worldName, why, now)); TShock.Log.ConsoleInfo($"Map auto-generated ({why})"); } - - protected override void Dispose(bool disposing) - { - if (disposing) - ServerApi.Hooks.GamePostInitialize.Deregister(this, OnPostInit); - base.Dispose(disposing); - } } } From 149504972876355e50c8d4b38a345c0a729aa27a Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 14:07:26 +0800 Subject: [PATCH 19/24] Update MapGenerator.cs --- WorldMapper/MapGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WorldMapper/MapGenerator.cs b/WorldMapper/MapGenerator.cs index c7541aa..e733e35 100644 --- a/WorldMapper/MapGenerator.cs +++ b/WorldMapper/MapGenerator.cs @@ -26,7 +26,7 @@ public static DirectBitmap Create(Rectangle? region = null) for (var y = 0; y < realRegion.Height; y++) { var tile = MapHelper.CreateMapTile(x + realRegion.X, y + realRegion.Y, byte.MaxValue); - var col = MapHelper.GetMapTileXnaColor(ref tile); + var col = MapHelper.GetMapTileXnaColor(tile); bitmap.SetPixel(x, y, new SKColor(col.R, col.G, col.B, col.A)); } From 9d3343acc42acb82242be99491663a1302e88c92 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 14:15:29 +0800 Subject: [PATCH 20/24] Update WorldMapper.csproj --- WorldMapper.csproj | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/WorldMapper.csproj b/WorldMapper.csproj index ae32646..f50bd2d 100644 --- a/WorldMapper.csproj +++ b/WorldMapper.csproj @@ -6,7 +6,7 @@ WorldMapper 1.0.0.0 latest - enable + disable true @@ -43,16 +43,13 @@ libs\ModFramework.dll false - - libs\MonoMod.RuntimeDetour.dll - false - + From 5bf3098f2b87b3d1a526a098fe5f3bb19b11def4 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 14:16:00 +0800 Subject: [PATCH 21/24] Update Plugin.cs --- WorldMapper/Plugin.cs | 64 +++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/WorldMapper/Plugin.cs b/WorldMapper/Plugin.cs index 05323a6..529876a 100644 --- a/WorldMapper/Plugin.cs +++ b/WorldMapper/Plugin.cs @@ -1,11 +1,12 @@ using System; using System.IO; using System.Reflection; +using MonoMod.RuntimeDetour; using Newtonsoft.Json; using Terraria; +using Terraria.IO; using TerrariaApi.Server; using TShockAPI; -using On.Terraria.IO; namespace WorldMapper { @@ -15,20 +16,20 @@ public class Plugin : TerrariaPlugin public override string Name => "World Mapper"; public override string Author => "James Puleo"; public override string Description => "Generates a PNG map of the entire world"; - public override Version Version => Assembly.GetExecutingAssembly().GetName().Version ?? new Version(1, 0, 0, 0); + public override Version Version => Assembly.GetExecutingAssembly().GetName().Version; - private Config? _config; + private Config _config; + private Hook _saveHook; public Plugin(Main game) : base(game) { } public override void Initialize() { _config = File.Exists(Config.DefaultPath) - ? JsonConvert.DeserializeObject(File.ReadAllText(Config.DefaultPath)) ?? new Config() + ? JsonConvert.DeserializeObject(File.ReadAllText(Config.DefaultPath)) : new Config(); - WorldFile.LoadWorld += OnLoadWorld; - WorldFile.SaveWorld += OnSaveWorld; + ServerApi.Hooks.GamePostInitialize.Register(this, OnPostInit); Commands.ChatCommands.Add(new Command("worldmapper.generatemap", args => { @@ -44,36 +45,57 @@ public override void Initialize() }, "generatemap")); } - protected override void Dispose(bool disposing) + private void OnPostInit(EventArgs args) { - if (disposing) - { - WorldFile.LoadWorld -= OnLoadWorld; - WorldFile.SaveWorld -= OnSaveWorld; - } - base.Dispose(disposing); + if (_config.SaveMapOnWorldLoad) + DoAutomaticGenerate("load"); + + if (_config.SaveMapOnWorldSave) + HookWorldSave(); } - private void OnLoadWorld(On.Terraria.IO.WorldFile.orig_LoadWorld orig, bool loadFromCloud) + private void HookWorldSave() { - orig(loadFromCloud); - if (_config?.SaveMapOnWorldLoad == true) - DoAutomaticGenerate("load"); + try + { + var saveMethod = typeof(WorldFile).GetMethod("SaveWorld", new[] { typeof(bool), typeof(bool) }); + if (saveMethod == null) + { + TShock.Log.ConsoleError("[WorldMapper] Could not find WorldFile.SaveWorld. Auto-save on world save disabled."); + return; + } + + var hookMethod = typeof(Plugin).GetMethod(nameof(OnSaveWorld), BindingFlags.NonPublic | BindingFlags.Instance); + _saveHook = new Hook(saveMethod, hookMethod, this); + } + catch (Exception ex) + { + TShock.Log.ConsoleError($"[WorldMapper] Failed to hook world save: {ex.Message}"); + } } - private void OnSaveWorld(On.Terraria.IO.WorldFile.orig_SaveWorld orig, bool useCloudSaving, bool resetTime) + private void OnSaveWorld(Action orig, bool useCloudSaving, bool resetTime) { orig(useCloudSaving, resetTime); - if (_config?.SaveMapOnWorldSave == true) - DoAutomaticGenerate("save"); + DoAutomaticGenerate("save"); } private void DoAutomaticGenerate(string why) { var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); using var bitmap = MapGenerator.Create(); - bitmap.Save(string.Format(_config!.MapFileNameFormat, Main.worldName, why, now)); + bitmap.Save(string.Format(_config.MapFileNameFormat, Main.worldName, why, now)); TShock.Log.ConsoleInfo($"Map auto-generated ({why})"); } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + ServerApi.Hooks.GamePostInitialize.Deregister(this, OnPostInit); + _saveHook?.Dispose(); + } + base.Dispose(disposing); + } } } From 05f6bc3fef714d49332e0075b21ec4515cc0d21d Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 14:16:41 +0800 Subject: [PATCH 22/24] Update MapGenerator.cs From 7ce06f66dcfe32f994e292de4b00f06c417953e0 Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 14:17:02 +0800 Subject: [PATCH 23/24] Update DirectBitmap.cs From 774a539da716dd3d7837bce29223315ae8efc4af Mon Sep 17 00:00:00 2001 From: AdvenRanises <110085960+AdvenRanises@users.noreply.github.com> Date: Sun, 21 Jun 2026 14:17:28 +0800 Subject: [PATCH 24/24] Update Config.cs