diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..9807f42
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,40 @@
+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 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
diff --git a/WorldMapper.csproj b/WorldMapper.csproj
new file mode 100644
index 0000000..f50bd2d
--- /dev/null
+++ b/WorldMapper.csproj
@@ -0,0 +1,55 @@
+
+
+
+ net9.0
+ WorldMapper
+ WorldMapper
+ 1.0.0.0
+ latest
+ disable
+ 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
+
+
+
+
+
+
+
+
+
+
+
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
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
+}
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
+}
diff --git a/WorldMapper/MapGenerator.cs b/WorldMapper/MapGenerator.cs
index 38b83d6..e733e35 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;
@@ -27,9 +26,9 @@ 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, 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
+}
diff --git a/WorldMapper/Plugin.cs b/WorldMapper/Plugin.cs
index d474be4..529876a 100644
--- a/WorldMapper/Plugin.cs
+++ b/WorldMapper/Plugin.cs
@@ -1,9 +1,10 @@
-using System;
+using System;
using System.IO;
using System.Reflection;
+using MonoMod.RuntimeDetour;
using Newtonsoft.Json;
-using OTAPI;
using Terraria;
+using Terraria.IO;
using TerrariaApi.Server;
using TShockAPI;
@@ -16,11 +17,11 @@ 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;
+ private Hook _saveHook;
- public Plugin(Main game) : base(game)
- {
- }
+ public Plugin(Main game) : base(game) { }
public override void Initialize()
{
@@ -28,44 +29,73 @@ public override void Initialize()
? JsonConvert.DeserializeObject(File.ReadAllText(Config.DefaultPath))
: new Config();
- Hooks.World.IO.PostLoadWorld += OnPostLoadWorld;
- Hooks.World.IO.PostSaveWorld += OnPostSaveWorld;
+ ServerApi.Hooks.GamePostInitialize.Register(this, OnPostInit);
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)
+ private void OnPostInit(EventArgs args)
{
- DoAutomaticGenerate("save");
+ if (_config.SaveMapOnWorldLoad)
+ DoAutomaticGenerate("load");
+
+ if (_config.SaveMapOnWorldSave)
+ HookWorldSave();
+ }
+
+ private void HookWorldSave()
+ {
+ 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 OnPostLoadWorld(bool loadfromcloud)
+ private void OnSaveWorld(Action orig, bool useCloudSaving, bool resetTime)
{
- DoAutomaticGenerate("load");
+ orig(useCloudSaving, resetTime);
+ DoAutomaticGenerate("save");
}
private void DoAutomaticGenerate(string why)
{
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
using var bitmap = MapGenerator.Create();
- Save(bitmap, 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})");
}
- private static void Save(DirectBitmap bitmap, string fileName)
+ protected override void Dispose(bool disposing)
{
- bitmap.Bitmap.Save(fileName);
- TShock.Log.ConsoleInfo($"Map generated and saved as {fileName}");
+ if (disposing)
+ {
+ ServerApi.Hooks.GamePostInitialize.Deregister(this, OnPostInit);
+ _saveHook?.Dispose();
+ }
+ base.Dispose(disposing);
}
}
-}
\ No newline at end of file
+}
diff --git a/WorldMapper/WorldMapper.csproj b/WorldMapper/WorldMapper.csproj
deleted file mode 100644
index 35e677e..0000000
--- a/WorldMapper/WorldMapper.csproj
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
- net48
- latest
-
-
-
-
- ..\..\tshock_test\Newtonsoft.Json.dll
-
-
- ..\..\tshock_test\OTAPI.dll
-
-
- ..\..\tshock_test\TerrariaServer.exe
-
-
- ..\..\tshock_test\ServerPlugins\TShockAPI.dll
-
-
-
-