diff --git a/AutoUpdatePlugin/AutoUpdatePlugin.csproj b/AutoUpdatePlugin/AutoUpdatePlugin.csproj
new file mode 100644
index 000000000..54932bd7c
--- /dev/null
+++ b/AutoUpdatePlugin/AutoUpdatePlugin.csproj
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/AutoUpdatePlugin/Plugin.cs b/AutoUpdatePlugin/Plugin.cs
new file mode 100644
index 000000000..ea819cd56
--- /dev/null
+++ b/AutoUpdatePlugin/Plugin.cs
@@ -0,0 +1,157 @@
+using Newtonsoft.Json;
+using System.IO.Compression;
+using Terraria;
+using TerrariaApi.Server;
+using TShockAPI;
+
+namespace AutoUpdatePlugin;
+
+[ApiVersion(2, 1)]
+public class Plugin : TerrariaPlugin
+{
+ public override string Name => "AutoUpdatePlugin";
+
+ public override Version Version => new(1, 6, 0, 2);
+
+ public override string Author => "少司命,Cai";
+
+ public override string Description => "自动更新你的插件!";
+
+ private const string ReleaseUrl = "https://github.com/Controllerdestiny/TShockPlugin/releases/download/V1.0.0.0/Plugins.zip";
+
+ private const string PUrl = "https://github.moeyy.xyz/";
+
+ private const string PluginsUrl = "https://raw.githubusercontent.com/Controllerdestiny/TShockPlugin/master/Plugins.json";
+
+ private static readonly HttpClient _httpClient = new();
+
+ private const string TempSaveDir = "TempFile";
+
+ private const string TempZipName = "Plugins.zip";
+
+ public Plugin(Main game) : base(game)
+ {
+
+ }
+
+ public override void Initialize()
+ {
+ Commands.ChatCommands.Add(new("AutoUpdatePlugin", CheckCmd, "cplugin"));
+ Commands.ChatCommands.Add(new("AutoUpdatePlugin", UpdateCmd, "uplugin"));
+ }
+
+ private void UpdateCmd(CommandArgs args)
+ {
+ try
+ {
+ var updates = GetUpdate();
+ if (updates.Count == 0)
+ {
+ args.Player.SendSuccessMessage("你的插件全是最新版本,无需更新哦~");
+ return;
+ }
+ args.Player.SendInfoMessage("正在下载最新插件包...");
+ DownLoadPlugin();
+ args.Player.SendInfoMessage("正在解压插件包...");
+ ExtractDirectoryZip();
+ args.Player.SendInfoMessage("正在升级插件...");
+ UpdatePlugin(updates);
+ args.Player.SendSuccessMessage("[更新完成]\n" + string.Join("\n", updates.Select(i => $"[{i.Name}] V{i.OldVersion} >>> V{i.NewVersion}")));
+ args.Player.SendSuccessMessage("重启服务器后插件生效!");
+ }
+ catch (Exception ex)
+ {
+ args.Player.SendErrorMessage("自动更新出现错误:" + ex.Message);
+ return;
+ }
+ }
+
+ private void CheckCmd(CommandArgs args)
+ {
+ try
+ {
+ var updates = GetUpdate();
+ if (updates.Count == 0)
+ {
+ args.Player.SendSuccessMessage("你的插件全是最新版本,无需更新哦~");
+ return;
+ }
+ args.Player.SendInfoMessage("[以下插件有新的版本更新]\n" + string.Join("\n", updates.Select(i => $"[{i.Name}] V{i.OldVersion} >>> V{i.NewVersion}")));
+ }
+ catch (Exception ex)
+ {
+ args.Player.SendErrorMessage("无法获取更新:" + ex.Message);
+ return;
+ }
+ }
+
+ #region 工具方法
+ private static List GetUpdate()
+ {
+ var plugins = GetPlugins();
+ HttpClient httpClient = new();
+ var response = httpClient.GetAsync(PUrl + PluginsUrl).Result;
+
+ if (!response.IsSuccessStatusCode)
+ throw new Exception("无法连接服务器");
+ var json = response.Content.ReadAsStringAsync().Result;
+ var latestPluginList = JsonConvert.DeserializeObject>(json) ?? new();
+ List pluginUpdateList = new();
+ foreach (var latestPluginInfo in latestPluginList)
+ foreach (var plugin in plugins)
+ if (plugin.Name == latestPluginInfo.Name && plugin.Version != latestPluginInfo.Version)
+ pluginUpdateList.Add(new PluginUpdateInfo(plugin.Name, plugin.Author, latestPluginInfo.Version, plugin.Version, plugin.Path, latestPluginInfo.Path));
+ return pluginUpdateList;
+ }
+
+ private static List GetPlugins()
+ {
+ List plugins = new();
+ foreach (var plugin in ServerApi.Plugins)
+ {
+ plugins.Add(new PluginVersionInfo()
+ {
+ AssemblyName = plugin.Plugin.GetType().Assembly.GetName().Name!,
+ Path = Path.Combine(ServerApi.ServerPluginsDirectoryPath, plugin.Plugin.GetType().Assembly.GetName().Name! + ".dll"),
+ Author = plugin.Plugin.Author,
+ Name = plugin.Plugin.Name,
+ Description = plugin.Plugin.Description,
+ Version = plugin.Plugin.Version.ToString()
+ });
+ }
+ return plugins;
+ }
+
+
+ private static void DownLoadPlugin()
+ {
+ DirectoryInfo directoryInfo = new(TempSaveDir);
+ if (!directoryInfo.Exists)
+ directoryInfo.Create();
+ HttpClient httpClient = new();
+ var zipBytes = httpClient.GetByteArrayAsync(PUrl + ReleaseUrl).Result;
+ File.WriteAllBytes(Path.Combine(directoryInfo.FullName, TempZipName), zipBytes);
+ }
+
+ private static void ExtractDirectoryZip()
+ {
+ DirectoryInfo directoryInfo = new(TempSaveDir);
+ ZipFile.ExtractToDirectory(Path.Combine(directoryInfo.FullName, TempZipName), Path.Combine(directoryInfo.FullName, "Plugins"), true);
+ }
+
+ private static void UpdatePlugin(List pluginUpdateInfos)
+ {
+ foreach (var pluginUpdateInfo in pluginUpdateInfos)
+ {
+ string sourcePath = Path.Combine(TempSaveDir, "Plugins", pluginUpdateInfo.RemotePath);
+ string destinationPath = Path.Combine(ServerApi.ServerPluginsDirectoryPath, pluginUpdateInfo.LocalPath);
+ // 确保目标目录存在
+ string destinationDirectory = Path.GetDirectoryName(destinationPath)!;
+ // 复制并覆盖文件
+ File.Copy(sourcePath, destinationPath, true);
+ }
+ if (Directory.Exists(TempSaveDir))
+ Directory.Delete(TempSaveDir, true);
+ }
+ #endregion
+}
diff --git a/AutoUpdatePlugin/PluginUpdateInfo.cs b/AutoUpdatePlugin/PluginUpdateInfo.cs
new file mode 100644
index 000000000..9938f5662
--- /dev/null
+++ b/AutoUpdatePlugin/PluginUpdateInfo.cs
@@ -0,0 +1,21 @@
+namespace AutoUpdatePlugin;
+
+public class PluginUpdateInfo
+{
+ public PluginUpdateInfo(string name, string author, string newVersion, string oldVersion, string localPath, string remotePath)
+ {
+ NewVersion = newVersion;
+ OldVersion = oldVersion;
+ Author = author;
+ Name = name;
+ LocalPath = localPath;
+ RemotePath = remotePath;
+ }
+ public string NewVersion { get; set; }
+ public string OldVersion { get; set; }
+ public string Author { get; set; }
+ public string Name { get; set; }
+ public string LocalPath { get; set; }
+ public string RemotePath { get; set; }
+
+}
diff --git a/AutoUpdatePlugin/PluginVersionInfo.cs b/AutoUpdatePlugin/PluginVersionInfo.cs
new file mode 100644
index 000000000..e0ffe5bc0
--- /dev/null
+++ b/AutoUpdatePlugin/PluginVersionInfo.cs
@@ -0,0 +1,16 @@
+namespace AutoUpdatePlugin;
+
+public class PluginVersionInfo
+{
+ public string Version { get; set; } = string.Empty;
+
+ public string Author { get; set; } = string.Empty;
+
+ public string Name { get; set; } = string.Empty;
+
+ public string Description { get; set; } = string.Empty;
+
+ public string Path { get; set; } = string.Empty;
+
+ public string AssemblyName { get; set; } = string.Empty;
+}
diff --git a/AutoUpdatePlugin/README.md b/AutoUpdatePlugin/README.md
new file mode 100644
index 000000000..d44575000
--- /dev/null
+++ b/AutoUpdatePlugin/README.md
@@ -0,0 +1,26 @@
+# AutoUpdatePlugin 自动更新插件
+
+- 作者: 少司命,Cai
+- 出处: 本仓库
+- 使用指令自动更新服务器的插件(仅本仓库)
+
+## 更新日志
+
+```
+暂无
+```
+
+## 指令
+
+| 语法 | 权限 | 说明 |
+| -------------- | :-----------------: | :------: |
+| /cplugin | AutoUpdatePlugin | 检查插件更新|
+| /uplugin | AutoUpdatePlugin | 一键升级插件(需要重启服务器)|
+## 配置
+
+```json
+暂无
+```
+## 反馈
+- 共同维护的插件库:https://github.com/Controllerdestiny/TShockPlugin
+- 国内社区trhub.cn 或 TShock官方群等
\ No newline at end of file
diff --git a/Plugin.sln b/Plugin.sln
index 9f02c861d..1d81342bc 100644
--- a/Plugin.sln
+++ b/Plugin.sln
@@ -184,6 +184,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimerKeeper", "TimerKeeper\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chameleon", "Chameleon\Chameleon.csproj", "{53CD6498-27DE-4C8C-A706-3CD2BD106D9E}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoUpdatePlugin", "AutoUpdatePlugin\AutoUpdatePlugin.csproj", "{9B88AC5E-E11C-4424-A5DC-5C1D094860B9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -896,6 +898,14 @@ Global
{53CD6498-27DE-4C8C-A706-3CD2BD106D9E}.Release|Any CPU.Build.0 = Release|Any CPU
{53CD6498-27DE-4C8C-A706-3CD2BD106D9E}.Release|x64.ActiveCfg = Release|Any CPU
{53CD6498-27DE-4C8C-A706-3CD2BD106D9E}.Release|x64.Build.0 = Release|Any CPU
+ {9B88AC5E-E11C-4424-A5DC-5C1D094860B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9B88AC5E-E11C-4424-A5DC-5C1D094860B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9B88AC5E-E11C-4424-A5DC-5C1D094860B9}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9B88AC5E-E11C-4424-A5DC-5C1D094860B9}.Debug|x64.Build.0 = Debug|Any CPU
+ {9B88AC5E-E11C-4424-A5DC-5C1D094860B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9B88AC5E-E11C-4424-A5DC-5C1D094860B9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9B88AC5E-E11C-4424-A5DC-5C1D094860B9}.Release|x64.ActiveCfg = Release|Any CPU
+ {9B88AC5E-E11C-4424-A5DC-5C1D094860B9}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/README.md b/README.md
index 9619e2028..673236c5f 100644
--- a/README.md
+++ b/README.md
@@ -132,7 +132,7 @@
| [Musicplayer](Musicplayer/README.md) | 简易音乐播放器 | 无 |
| [TimerKeeper](TimerKeeper/README.md) | 保存计时器状态 | 无 |
| [Chameleon](Chameleon/README.md) | 进服前登录 | 无 |
-
+| [AutoUpdatePlugin](AutoUpdatePlugin/README.md) | 一键自动更新插件 | 无 |
## 代码贡献
diff --git a/ServerTools/ClearPlayersItem.cs b/ServerTools/ClearPlayersItem.cs
deleted file mode 100644
index c31a022e5..000000000
--- a/ServerTools/ClearPlayersItem.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using Terraria;
-using Microsoft.Xna.Framework;
-using TShockAPI;
-
-namespace ServerTools
-{
- internal class ClearPlayersItem
- {
- #region 清理盔甲组逻辑
- public void ClearItem(Item[] items, TSPlayer tSPlayer)
- {
- for (int i = 0; i < 10; i++)
- {
- foreach (Item item in items)
- {
- if (!tSPlayer.TPlayer.armor[i].IsAir && tSPlayer.TPlayer.armor[i].type == item.type)
- {
- tSPlayer.TPlayer.armor[i].TurnToAir();
- tSPlayer.SendData(PacketTypes.PlayerSlot, "", tSPlayer.Index, Terraria.ID.PlayerItemSlotID.Armor0 + i);
- }
- }
- }
- }
- #endregion
- }
-}
diff --git a/ServerTools/Plugin.cs b/ServerTools/Plugin.cs
index e08b5926a..80f5b1f39 100644
--- a/ServerTools/Plugin.cs
+++ b/ServerTools/Plugin.cs
@@ -1,10 +1,6 @@
using Microsoft.Xna.Framework;
using MonoMod.RuntimeDetour;
using Newtonsoft.Json;
-using NuGet.Configuration;
-using NuGet.Protocol.Plugins;
-using System.Linq;
-using Terraria.DataStructures;
using Terraria;
using Terraria.GameContent.Creative;
using TerrariaApi.Server;
@@ -22,7 +18,7 @@ public partial class Plugin : TerrariaPlugin
public override string Name => "ServerTools";// 插件名字
- public override Version Version => new(1, 1, 7, 0);// 插件版本
+ public override Version Version => new(1, 1, 7, 1);// 插件版本
private static Config Config = new();
@@ -38,8 +34,6 @@ public partial class Plugin : TerrariaPlugin
public static Hook CmdHook;
- private static ClearPlayersItem clear = new();
-
public Plugin(Main game) : base(game)
{
@@ -112,30 +106,11 @@ private void OnUpdate(object? sender, GetDataHandlers.PlayerUpdateEventArgs e)
e.Player.SetBuff(156, 180, true);
TShock.Utils.Broadcast($"[ServerTools] 玩家 [{e.Player.Name}] 因多饰品被冻结3秒,自动施行清理多饰品装备[i:{keepArmor.netID}]", Color.DarkRed);
}
- if (ArmorGroup.Any() && TimerCount % 20 == 0)
- clear.ClearItem(ArmorGroup.ToArray(), e.Player);
-
- if (Config.KeepArmor2 && !Main.hardMode) { Clear7Item(e.Player); }
- }
+ if (ArmorGroup.Any())
+ Utils.ClearItem(ArmorGroup.ToArray(), e.Player);
- private static void Clear7Item(TSPlayer args)
- {
- if (!args.TPlayer.armor[8].IsAir && TimerCount % 20 == 0)
- {
- Item i = args.TPlayer.armor[8];
- GiveItem(args, i.type, i.stack, i.prefix);
- args.TPlayer.armor[8].TurnToAir();
- args.SendData(PacketTypes.PlayerSlot, "", args.Index, Terraria.ID.PlayerItemSlotID.Armor0 + 8);
- TShock.Utils.Broadcast($"[ServerTools] 世界未开启困难模式,禁止玩家 [{args.Name}]使用恶魔心饰品栏", Color.DarkRed);
- }
- }
-
- private static void GiveItem(TSPlayer p, int type, int stack, int prefix = 0)
- {
- int num = Item.NewItem(new EntitySource_DebugCommand(), (int)p.TPlayer.Center.X, (int)p.TPlayer.Center.Y, p.TPlayer.width, p.TPlayer.height, type, stack, true, prefix, true, false);
- Main.item[num].playerIndexTheItemIsReservedFor = p.Index;
- p.SendData(PacketTypes.ItemDrop, "", num, 1f, 0f, 0f, 0);
- p.SendData(PacketTypes.ItemOwner, null, num, 0f, 0f, 0f, 0);
+ if (Config.KeepArmor2 && !Main.hardMode)
+ Utils.Clear7Item(e.Player);
}
#endregion
diff --git a/ServerTools/Utils.cs b/ServerTools/Utils.cs
new file mode 100644
index 000000000..f32625131
--- /dev/null
+++ b/ServerTools/Utils.cs
@@ -0,0 +1,38 @@
+using Terraria;
+using Microsoft.Xna.Framework;
+using TShockAPI;
+
+namespace ServerTools;
+
+internal class Utils
+{
+ public static void Clear7Item(TSPlayer Player)
+ {
+ if (!Player.TPlayer.armor[8].IsAir)
+ {
+ Item item = Player.TPlayer.armor[8];
+ Player.GiveItem(item.type, item.stack, item.prefix);
+ Player.TPlayer.armor[8].TurnToAir();
+ Player.SendData(PacketTypes.PlayerSlot, "", Player.Index, Terraria.ID.PlayerItemSlotID.Armor0 + 8);
+ TShock.Utils.Broadcast($"[ServerTools] 世界未开启困难模式,禁止玩家 [{Player.Name}]使用恶魔心饰品栏", Color.DarkRed);
+ }
+ }
+
+
+ #region 清理盔甲组逻辑
+ public static void ClearItem(Item[] items, TSPlayer tSPlayer)
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ foreach (Item item in items)
+ {
+ if (!tSPlayer.TPlayer.armor[i].IsAir && tSPlayer.TPlayer.armor[i].type == item.type)
+ {
+ tSPlayer.TPlayer.armor[i].TurnToAir();
+ tSPlayer.SendData(PacketTypes.PlayerSlot, "", tSPlayer.Index, Terraria.ID.PlayerItemSlotID.Armor0 + i);
+ }
+ }
+ }
+ }
+ #endregion
+}