From b9ce36e527760c9541e0b64222a8237345cf5a64 Mon Sep 17 00:00:00 2001 From: xien <2383759126@qq.com> Date: Sun, 29 Jun 2025 21:47:40 +0800 Subject: [PATCH 01/20] =?UTF-8?q?update:TPallow=E9=BB=98=E8=AE=A4=E5=85=81?= =?UTF-8?q?=E8=AE=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/EssentialsPlus/Db/TpAllowManager.cs | 39 +++++++++++++------------ src/EssentialsPlus/EssentialsPlus.cs | 2 +- src/EssentialsPlus/README.md | 2 ++ 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/EssentialsPlus/Db/TpAllowManager.cs b/src/EssentialsPlus/Db/TpAllowManager.cs index a02c847c9..2ca133f82 100644 --- a/src/EssentialsPlus/Db/TpAllowManager.cs +++ b/src/EssentialsPlus/Db/TpAllowManager.cs @@ -48,30 +48,23 @@ public bool ToggleTpAllow(TSPlayer player) if (existing == null) { - newState = true; + // 默认允许 + newState = false; this.TpAllows.Add(new TpAllow(player.Name, newState)); - - if (this.db.Query("INSERT OR REPLACE INTO TpAllows VALUES (@0, @1)", - player.Name, newState ? 1 : 0) > 0) - { - player.TPAllow = newState; - return true; - } - return false; } else { newState = !existing.IsEnabled; existing.IsEnabled = newState; + } - if (this.db.Query("UPDATE TpAllows SET IsEnabled = @0 WHERE Name = @1", - newState ? 1 : 0, player.Name) > 0) - { - player.TPAllow = newState; - return true; - } - return false; + if (this.db.Query("INSERT OR REPLACE INTO TpAllows VALUES (@0, @1)", + player.Name, newState ? 1 : 0) > 0) + { + player.TPAllow = newState; + return true; } + return false; } catch (Exception ex) { @@ -136,12 +129,22 @@ public bool IsTpAllowed(TSPlayer player) player.TPAllow = isEnabled; return isEnabled; } + + // 未找到记录时,默认允许传送并添加记录 + var defaultAllowed = true; + this.TpAllows.Add(new TpAllow(player.Name, defaultAllowed)); + this.db.Query("INSERT INTO TpAllows (Name, IsEnabled) VALUES (@0, @1)", + player.Name, defaultAllowed ? 1 : 0); + player.TPAllow = defaultAllowed; + return defaultAllowed; } catch (Exception ex) { TShock.Log.Error($"查询玩家 {player.Name} 的传送权限时发生错误: {ex}"); } - player.TPAllow = false; - return false; + + // 异常情况下保持默认允许(可选,根据需求决定) + player.TPAllow = true; + return true; } } \ No newline at end of file diff --git a/src/EssentialsPlus/EssentialsPlus.cs b/src/EssentialsPlus/EssentialsPlus.cs index b140fbf6b..f61148aaf 100644 --- a/src/EssentialsPlus/EssentialsPlus.cs +++ b/src/EssentialsPlus/EssentialsPlus.cs @@ -24,7 +24,7 @@ public class EssentialsPlus : LazyPlugin public override string Description => GetString("增强版Essentials"); public override string Name => System.Reflection.Assembly.GetExecutingAssembly().GetName().Name!; - public override Version Version => new Version(1, 0, 9); + public override Version Version => new Version(1, 1, 0); public EssentialsPlus(Main game) diff --git a/src/EssentialsPlus/README.md b/src/EssentialsPlus/README.md index 9a197b910..9d325d845 100644 --- a/src/EssentialsPlus/README.md +++ b/src/EssentialsPlus/README.md @@ -75,6 +75,8 @@ ``` ## 更新日志 +### v1.1.0 +- Tpallow默认允许传送 ### v1.0.9 - 数据库记录玩家tpallow状态 ### v1.0.7 From d85cb337aefa1fff85262d0fae9c8f843724eff8 Mon Sep 17 00:00:00 2001 From: xien <2383759126@qq.com> Date: Sun, 29 Jun 2025 21:54:53 +0800 Subject: [PATCH 02/20] add:TrCDK --- Plugin.sln | 10 + src/TrCDK/Data.cs | 93 ++++++++++ src/TrCDK/Main.cs | 263 +++++++++++++++++++++++++++ src/TrCDK/README.en-US.md | 36 ++++ src/TrCDK/README.md | 33 ++++ src/TrCDK/TSPlayerExtensions.cs | 59 ++++++ src/TrCDK/TShockCommandExtensions.cs | 24 +++ src/TrCDK/TrCDK.csproj | 5 + src/TrCDK/TypeExtensions.cs | 27 +++ src/TrCDK/manifest.json | 11 ++ 10 files changed, 561 insertions(+) create mode 100644 src/TrCDK/Data.cs create mode 100644 src/TrCDK/Main.cs create mode 100644 src/TrCDK/README.en-US.md create mode 100644 src/TrCDK/README.md create mode 100644 src/TrCDK/TSPlayerExtensions.cs create mode 100644 src/TrCDK/TShockCommandExtensions.cs create mode 100644 src/TrCDK/TrCDK.csproj create mode 100644 src/TrCDK/TypeExtensions.cs create mode 100644 src/TrCDK/manifest.json diff --git a/Plugin.sln b/Plugin.sln index e5edfab95..c60bfb8ec 100644 --- a/Plugin.sln +++ b/Plugin.sln @@ -282,6 +282,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoClassificationQuickStac EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGen", "src\SourceGen\SourceGen.csproj", "{E5E60819-4B13-4982-B345-358A4DBE9D3B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrCDK", "src\TrCDK\TrCDK.csproj", "{D06CC8C7-7B77-428B-B5BA-A690C17DE382}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1386,6 +1388,14 @@ Global {E5E60819-4B13-4982-B345-358A4DBE9D3B}.Release|Any CPU.Build.0 = Release|Any CPU {E5E60819-4B13-4982-B345-358A4DBE9D3B}.Release|x64.ActiveCfg = Release|Any CPU {E5E60819-4B13-4982-B345-358A4DBE9D3B}.Release|x64.Build.0 = Release|Any CPU + {D06CC8C7-7B77-428B-B5BA-A690C17DE382}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D06CC8C7-7B77-428B-B5BA-A690C17DE382}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D06CC8C7-7B77-428B-B5BA-A690C17DE382}.Debug|x64.ActiveCfg = Debug|Any CPU + {D06CC8C7-7B77-428B-B5BA-A690C17DE382}.Debug|x64.Build.0 = Debug|Any CPU + {D06CC8C7-7B77-428B-B5BA-A690C17DE382}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D06CC8C7-7B77-428B-B5BA-A690C17DE382}.Release|Any CPU.Build.0 = Release|Any CPU + {D06CC8C7-7B77-428B-B5BA-A690C17DE382}.Release|x64.ActiveCfg = Release|Any CPU + {D06CC8C7-7B77-428B-B5BA-A690C17DE382}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/TrCDK/Data.cs b/src/TrCDK/Data.cs new file mode 100644 index 000000000..45b24222c --- /dev/null +++ b/src/TrCDK/Data.cs @@ -0,0 +1,93 @@ +using System.Data; +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Data.Sqlite; +using static Terraria.ID.ContentSamples.CreativeHelper; + +namespace TrCDK; + +public class CDK +{ + public string Cdkname { get; set; } = ""; + public int Usetime { get; set; } + public long Utiltime { get; set; } + public string Grouplimit { get; set; } = ""; + public string Playerlimit { get; set; } = ""; + public string Used { get; set; } = ""; + public string Cmds { get; set; } = ""; + } +internal class Data +{ + public static SqliteConnection? DB; + const string path = "tshock/TrCDK.sqlite"; + public static void Init() + { + DB = new SqliteConnection($"Data Source={path};"); + DB.Open(); + Command("create table if not exists Data(CDKname text,Usetime int(32),Utiltime int(64),Grouplimit text,Playerlimit text,Used text, Cmds text)"); + } + public static SqliteDataReader Command(string cmd) + { + return new SqliteCommand(cmd, DB).ExecuteReader(); + } + public static bool Insert(string CDKname, int Usetime, long Utiltime, string Grouplimit, string Playerlimit, string Cmds) + { + using (var reader = Command($"select * from Data where CDKname='{CDKname}'")) + { + while (reader.Read()) + { + if (reader.GetString(0) == CDKname) + { + return false; + } + } + } + Command($"insert into Data(CDKname,Usetime,Utiltime,Grouplimit,Playerlimit,Used,Cmds)values('{CDKname}','{Usetime}','{Utiltime}','{Grouplimit}','{Playerlimit}','','{Cmds}')"); + return true; + } + public static void Update(string CDKname, int Usetime, long Utiltime, string Grouplimit, string Playerlimit, string Used, string Cmds) + { + using (var reader = Command($"select * from Data where CDKname='{CDKname}'")) + { + while (reader.Read()) + { + if (reader.GetString(0) == CDKname) + { + Command($"UPDATE Data SET Usetime='{Usetime}',Utiltime='{Utiltime}',Grouplimit='{Grouplimit}',Playerlimit='{Playerlimit}',Used='{Used}',Cmds='{Cmds}' WHERE CDKname='{CDKname}'"); + } + } + } + } + public static CDK GetData(string name) + { + var Res = new CDK(); + using(var reader=Command($"select * from Data where CDKname='{name}'")) + { + while (reader.Read()) + { + Res = new CDK(){ Cdkname = reader.GetString(0), Usetime = reader.GetInt32(1), Utiltime = reader.GetInt64(2), Grouplimit = reader.GetString(3), Playerlimit = reader.GetString(4), Used = reader.GetString(5), Cmds = reader.GetString(6)}; + + } + } + return Res; + } + public static CDK[] GetAllData() + { + var Res = new List(); + using (var reader = Command("select * from Data")) + { + while (reader.Read()) + { + Res.Add (new CDK() { Cdkname = reader.GetString(0), Usetime = reader.GetInt32(1), Utiltime = reader.GetInt64(2), Grouplimit = reader.GetString(3), Playerlimit = reader.GetString(4), Used = reader.GetString(5), Cmds = reader.GetString(6) }); + + } + } + return Res.ToArray(); + } + public static bool DelCDK(string name) + { + Command($"DELETE FROM Data where CDKname='{name}'"); + return true; + } + } diff --git a/src/TrCDK/Main.cs b/src/TrCDK/Main.cs new file mode 100644 index 000000000..2d6500087 --- /dev/null +++ b/src/TrCDK/Main.cs @@ -0,0 +1,263 @@ +using TShockAPI; +using Terraria; +using TerrariaApi.Server; +using System.Globalization; +using Microsoft.Xna.Framework; + +namespace TrCDK; + +[ApiVersion(2, 1)] +public class TrCDK : TerrariaPlugin +{ + public override string Author => "Jonesn"; + public override string Description => GetString("CDK系统"); + public override string Name => "TrCDK"; + public override Version Version => new Version(1, 0, 0, 0); + public TrCDK(Main game) : base(game) { } + + public override void Initialize() + { + Data.Init(); + // 添加所有CDK相关指令 + Commands.ChatCommands.Add(new Command("cdk.use", this.UseCDK, "cdk")); + Commands.ChatCommands.Add(new Command("cdk.admin.loadall", this.LoadAllCDK, "cdkloadall")); + Commands.ChatCommands.Add(new Command("cdk.admin.add", this.AddCDKCmd, "cdkadd")); + Commands.ChatCommands.Add(new Command("cdk.admin.del", this.DelCDKCmd, "cdkdel")); + Commands.ChatCommands.Add(new Command("cdk.admin.update", this.UpdateCDKCmd, "cdkupdate")); + Commands.ChatCommands.Add(new Command("cdk.admin.give", this.GiveCDKCmd, "cdkgive")); + } + + protected override void Dispose(bool Disposing) + { + if (Disposing) + { + Commands.ChatCommands.RemoveAll(c => c.CommandDelegate == this.UseCDK || + c.CommandDelegate == this.LoadAllCDK || + c.CommandDelegate == this.AddCDKCmd || + c.CommandDelegate == this.DelCDKCmd || + c.CommandDelegate == this.UpdateCDKCmd || + c.CommandDelegate == this.GiveCDKCmd); + } + base.Dispose(Disposing); + } + + // 添加CDK指令 + private void AddCDKCmd(CommandArgs args) + { + if (args.Parameters.Count < 6) + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:用法: /cdkadd <使用次数> <过期时间> <组限制> <玩家限制> <指令>]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:过期时间格式: yyyy-MM-ddThh:mm (例: 2025-12-31T23:59)]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:组限制和玩家限制可以用逗号分隔多个,不限制则填 none]")); + return; + } + + var cdkName = args.Parameters[0]; + if (!int.TryParse(args.Parameters[1], out var useTime)) + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:使用次数必须是数字!]")); + return; + } + + var utilTime = args.Parameters[2]; + var groupLimit = args.Parameters[3] == "none" ? "" : args.Parameters[3]; + var playerLimit = args.Parameters[4] == "none" ? "" : args.Parameters[4]; + + // 组合剩余参数作为指令 + var cmds = string.Join(" ", args.Parameters.Skip(5)); + + try + { + var timeForInfo = new DateTimeFormatInfo + { + ShortDatePattern = "yyyy-MM-ddThh:mm" + }; + var time = Convert.ToDateTime(utilTime, timeForInfo); + + if (Data.Insert(cdkName, useTime, Convert.ToDateTime(time.ToString()).Ticks, groupLimit, playerLimit, cmds)) + { + args.Player.SendSuccessMessage(GetString($"[c/00FF00:【CDK管理】]\n[c/ffd700:成功添加CDK: {cdkName}]")); + } + else + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:添加CDK失败,可能已存在同名CDK]")); + } + } + catch (Exception ex) + { + args.Player.SendErrorMessage(GetString($"[c/FF0000:【CDK管理】]\n[c/ffd700:时间格式错误: {ex.Message}]")); + } + } + + // 删除CDK指令 + private void DelCDKCmd(CommandArgs args) + { + if (args.Parameters.Count < 1) + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:用法: /cdkdel ]")); + return; + } + + var cdkName = args.Parameters[0]; + Data.DelCDK(cdkName); + args.Player.SendSuccessMessage(GetString($"[c/00FF00:【CDK管理】]\n[c/ffd700:已删除CDK: {cdkName}]")); + } + + // 更新CDK指令 + private void UpdateCDKCmd(CommandArgs args) + { + if (args.Parameters.Count < 7) + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:用法: /cdkupdate <使用次数> <过期时间> <组限制> <玩家限制> <已使用玩家> <指令>]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:过期时间格式: yyyy-MM-ddThh:mm]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:不需要修改的项目可以填 none]")); + return; + } + + var cdkName = args.Parameters[0]; + if (!int.TryParse(args.Parameters[1], out var useTime)) + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:使用次数必须是数字!]")); + return; + } + + var utilTime = args.Parameters[2]; + var groupLimit = args.Parameters[3] == "none" ? "" : args.Parameters[3]; + var playerLimit = args.Parameters[4] == "none" ? "" : args.Parameters[4]; + var used = args.Parameters[5] == "none" ? "" : args.Parameters[5]; + var cmds = string.Join(" ", args.Parameters.Skip(6)); + + try + { + var timeForInfo = new DateTimeFormatInfo + { + ShortDatePattern = "yyyy-MM-ddThh:mm" + }; + var time = Convert.ToDateTime(utilTime, timeForInfo); + + Data.Update(cdkName, useTime, Convert.ToDateTime(time.ToString()).Ticks, groupLimit, playerLimit, used, cmds); + args.Player.SendSuccessMessage(GetString($"[c/00FF00:【CDK管理】]\n[c/ffd700:成功更新CDK: {cdkName}]")); + } + catch (Exception ex) + { + args.Player.SendErrorMessage(GetString($"[c/FF0000:【CDK管理】]\n[c/ffd700:时间格式错误: {ex.Message}]")); + } + } + + // 加载所有CDK指令 + private void LoadAllCDK(CommandArgs args) + { + var allcdk = Data.GetAllData(); + args.Player.SendInfoMessage(GetString("[c/00FF00:【CDK管理】]\n[c/ffd700:=== 所有CDK列表 ===]")); + + foreach (var cdk in allcdk) + { + var cdkObj = (CDK) cdk; + var expireTime = new DateTime(cdkObj.Utiltime); + args.Player.SendInfoMessage(GetString($"[c/ffd700:CDK: {cdkObj.Cdkname} | 剩余次数: {cdkObj.Usetime} | 过期时间: {expireTime:yyyy-MM-dd HH:mm}]")); + } + } + + // 给予CDK奖励指令 + private void GiveCDKCmd(CommandArgs args) + { + if (args.Parameters.Count < 2) + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:用法: /cdkgive <玩家名> <指令列表>]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:指令列表用逗号分隔,例: give [plr] 1 1,heal [plr]]")); + return; + } + + var playerName = args.Parameters[0]; + var cmds = string.Join(" ", args.Parameters.Skip(1)); + + var players = TShockAPI.TSPlayer.FindByNameOrID(playerName); + if (players.Count == 0) + { + args.Player.SendErrorMessage(GetString($"[c/FF0000:【CDK管理】]\n[c/ffd700:找不到玩家: {playerName}]")); + return; + } + + if (players.Count > 1) + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:找到多个匹配的玩家,请使用更精确的名称]")); + return; + } + + var targetPlayer = players[0]; + if (cmds != "") + { + var cmdArray = cmds.Split(','); + foreach (var cmd in cmdArray) + { + var processedCmd = cmd.Replace("[plr]", targetPlayer.Name).Trim(); + targetPlayer.PermissionlessInvoke(processedCmd); + } + args.Player.SendSuccessMessage(GetString($"[c/00FF00:【CDK管理】]\n[c/ffd700:已给予玩家 {targetPlayer.Name} CDK奖励]")); + } + else + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:指令不能为空]")); + } + } + + // 使用CDK指令(玩家用) + private void UseCDK(CommandArgs args) + { + if (args.Parameters.Count == 0) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:指令:/cdk CDK兑换码 - 兑换一个CDK礼包]")); + } + else + { + var cdk1 = Data.GetData(args.Parameters[0]); + if (cdk1 == null) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:CDK不存在或已失效]")); + return; + } + + if (cdk1.Playerlimit != "") + { + if (!cdk1.Playerlimit.Contains(args.Player.Name)) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你不在该CDK的领取名单中]")); + return; + } + } + if (cdk1.Grouplimit != "") + { + if (!cdk1.Grouplimit.Contains(args.Player.Group.Name)) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你所在的组不能领取该CDK]")); + return; + } + } + if (cdk1.Usetime < 1) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你手慢了, 该CDK已经被领完]")); + return; + } + var time2 = Convert.ToDateTime(DateTime.Now.ToString()).Ticks; + var min = (cdk1.Utiltime - time2) / 10000000; + if (min <= 0) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你来晚了, 该CDK已经过期了]")); + return; + } + if (cdk1.Used.Contains(args.Player.Name)) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:已经领取过了,不能太贪心哦]")); + return; + } + var strcmd = cdk1.Cmds.Replace("[plr]", args.Player.Name); + var cmds = strcmd.Split(','); + foreach (var cmd in cmds) + { + args.Player.PermissionlessInvoke(cmd); + } + Data.Update(cdk1.Cdkname, cdk1.Usetime - 1, cdk1.Utiltime, cdk1.Grouplimit, cdk1.Playerlimit, (cdk1.Used == "") ? args.Player.Name : cdk1.Used + "," + args.Player.Name, cdk1.Cmds); + args.Player.SendSuccessMessage(GetString("[c/00FF00:【CDK】]\n[c/ffd700:CDK兑换成功!]")); + } + } +} \ No newline at end of file diff --git a/src/TrCDK/README.en-US.md b/src/TrCDK/README.en-US.md new file mode 100644 index 000000000..6fbd58d8e --- /dev/null +++ b/src/TrCDK/README.en-US.md @@ -0,0 +1,36 @@ +# TrCDK CDK +- Author: Jonesn +- Source: ARK Server +- Can return the player's last death location with customizable cooldown time. +- Administrators can create, delete, and update CDKs, and players can redeem (execute commands) rewards using valid CDKs. + + +## Commands +| Syntax | Permission | Description | +| --- | --- | --- | +| /cdk `` | cdk.use | Players redeem CDK packages. | +| /cdkloadall | cdk.admin.loadall | Display all CDK lists. | +| /cdkadd `` `` `` `` `` `` | cdk.admin.add | Add a new CDK. | +| /cdkdel `` | cdk.admin.del | Delete the specified CDK. | +| /cdkupdate `` `` `` `` `` `` `` | cdk.admin.update | Update CDK information. | +| /cdkgive `` `` | cdk.admin.give | Grant CDK rewards to a player. | + + +### Notes +- **/cdkadd and /cdkupdate**: Expiry time format is `yyyy-MM-ddThh:mm`, e.g., `2024-12-31T23:59`. Group and player limits are separated by commas; use `none` for no restrictions. +- **/cdkgive**: Command lists are separated by commas, e.g., `/give 4956 [plr] 1,/heal [plr]`. + + +## Configuration +> Database file location: tshock/TrCDK.sqlite + + +## Change Log +### v1.0.0.0 +Initial release. + + +## Feedback +- Priority: Submit issues -> Shared plugin library: https://github.com/UnrealMultiple/TShockPlugin +- Secondary: TShock Official Group: 816771079 +- Less frequently checked but acceptable: Chinese communities trhub.cn, bbstr.net \ No newline at end of file diff --git a/src/TrCDK/README.md b/src/TrCDK/README.md new file mode 100644 index 000000000..12ea07221 --- /dev/null +++ b/src/TrCDK/README.md @@ -0,0 +1,33 @@ +# TrCDK CDK系统插件 +- 作者: Jonesn +- 出处: ARK服务器 +- 可以返回上次玩家的死亡地点,可自定义冷却时间 +- 管理员能创建、删除、更新 CDK,玩家可使用有效 CDK 兑换(执行指令)奖励。 + +## 指令 +| 语法 | 权限 | 说明 | +| --- | --- | --- | +| /cdk `` | cdk.use | 玩家兑换 CDK 礼包 | +| /cdkloadall | cdk.admin.loadall | 显示所有 CDK 列表 | +| /cdkadd `` `<使用次数>` `<过期时间>` `<组限制>` `<玩家限制>` `<指令>` | cdk.admin.add | 添加新 CDK | +| /cdkdel `` | cdk.admin.del | 删除指定 CDK | +| /cdkupdate `` `<使用次数>` `<过期时间>` `<组限制>` `<玩家限制>` `<已使用玩家>` `<指令>` | cdk.admin.update | 更新 CDK 信息 | +| /cdkgive `<玩家名>` `<指令列表>` | cdk.admin.give | 给玩家 CDK 奖励 | + +### 注意事项 +- **/cdkadd 和 /cdkupdate**:过期时间格式 `yyyy-MM-ddThh:mm`,如 `2024-12-31T23:59`。组和玩家限制用逗号分隔,不限制填 `none`。 +- **/cdkgive**:指令列表用逗号分隔,如 `/give 4956 [plr] 1,/heal [plr]`。 + +## 配置 +> 数据库文件位置:tshock/TrCDK.sqlite + + +## 更新日志 + +### v1.0.0.0 +初始版本发布。 + +## 反馈 +- 优先发issued -> 共同维护的插件库:https://github.com/UnrealMultiple/TShockPlugin +- 次优先:TShock官方群:816771079 +- 大概率看不到但是也可以:国内社区trhub.cn ,bbstr.net \ No newline at end of file diff --git a/src/TrCDK/TSPlayerExtensions.cs b/src/TrCDK/TSPlayerExtensions.cs new file mode 100644 index 000000000..535e18174 --- /dev/null +++ b/src/TrCDK/TSPlayerExtensions.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework; +using TShockAPI; + +namespace TrCDK +{ + public static class TSPlayerExtensions + { + public static void SendMessageFormat(this TSPlayer Player, Color Colour, string MessageFormat, params object[] args) + { + Player.SendMessage(string.Format(MessageFormat, args), Colour); + } + + public static bool PermissionlessInvoke(this TSPlayer player, string text, bool silent = false) + { + if (string.IsNullOrEmpty(text)) + { + return false; + } + var text2 = text.Remove(0, 1); + var list = typeof(Commands).CallPrivateMethod>(StaticMember: true, "ParseParameters", new object[1] { text2 }); + if (list.Count < 1) + { + return false; + } + var cmdName = list[0].ToLower(); + list.RemoveAt(0); + var enumerable = Commands.ChatCommands.Where((Command c) => c.HasAlias(cmdName)); + if (enumerable.Count() == 0) + { + if (player.AwaitingResponse.ContainsKey(cmdName)) + { + Action action = player.AwaitingResponse[cmdName]; + player.AwaitingResponse.Remove(cmdName); + action(new CommandArgs(text2, player, list)); + return true; + } + player.SendErrorMessage(GetString("Ч./helpȡЧб.")); + return true; + } + foreach (var item in enumerable) + { + if (!item.AllowServer && !player.RealPlayer) + { + player.SendErrorMessage(GetString("Ϸʹ.")); + continue; + } + if (item.DoLog && !silent) + { + TShock.Utils.SendLogs(player.Name + GetString(" ִ: /") + text2 + ".", Color.Red); + } + item.RunWithoutPermissions(text2, player, list); + } + return true; + } + } +} diff --git a/src/TrCDK/TShockCommandExtensions.cs b/src/TrCDK/TShockCommandExtensions.cs new file mode 100644 index 000000000..18d1938f7 --- /dev/null +++ b/src/TrCDK/TShockCommandExtensions.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using TShockAPI; + +namespace TrCDK +{ + public static class TShockCommandExtensions + { + public static bool RunWithoutPermissions(this Command cmd, string msg, TSPlayer ply, List parms, bool silent = false) + { + try + { + var commandDelegate = cmd.CommandDelegate; + commandDelegate(new CommandArgs(msg, silent, ply, parms)); + } + catch (Exception ex) + { + ply.SendErrorMessage(GetString("ִָʧܣϵԱ")); + TShock.Log.Error(ex.ToString()); + } + return true; + } + } +} diff --git a/src/TrCDK/TrCDK.csproj b/src/TrCDK/TrCDK.csproj new file mode 100644 index 000000000..1bebdd9fc --- /dev/null +++ b/src/TrCDK/TrCDK.csproj @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/TrCDK/TypeExtensions.cs b/src/TrCDK/TypeExtensions.cs new file mode 100644 index 000000000..d4850288e --- /dev/null +++ b/src/TrCDK/TypeExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Reflection; + +namespace TrCDK +{ + public static class TypeExtensions + { + public static T CallPrivateMethod(this Type _type, bool StaticMember, string Name, params object[] Params) + { + var bindingFlags = BindingFlags.NonPublic; + bindingFlags |= (StaticMember ? BindingFlags.Static : BindingFlags.Instance); + var method = _type.GetMethod(Name, bindingFlags); + return (T)method.Invoke(StaticMember ? null : _type, Params); + } + + public static T GetPrivateField(this Type type, object Instance, string Name, params object[] Param) + { + var bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; + var field = type.GetField(Name, bindingAttr); + if (field == null) + { + return default(T); + } + return (T)field.GetValue(Instance); + } + } +} diff --git a/src/TrCDK/manifest.json b/src/TrCDK/manifest.json new file mode 100644 index 000000000..e41c9eaf9 --- /dev/null +++ b/src/TrCDK/manifest.json @@ -0,0 +1,11 @@ +{ + "README.en-US": { + "Description": "Enter redemption code (CDK) to execute commands" + }, + "README.es-ES": { + "Description": "Introduce el código de canje (CDK) para ejecutar comandos" + }, + "README": { + "Description": "输入兑换码执行指令" + } +} \ No newline at end of file From b77b02554ae38d5eaf87770fd4609ca6080bf5ee Mon Sep 17 00:00:00 2001 From: xien <2383759126@qq.com> Date: Sun, 29 Jun 2025 21:56:49 +0800 Subject: [PATCH 03/20] doc --- src/TrCDK/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TrCDK/README.md b/src/TrCDK/README.md index 12ea07221..7d632b88e 100644 --- a/src/TrCDK/README.md +++ b/src/TrCDK/README.md @@ -3,6 +3,7 @@ - 出处: ARK服务器 - 可以返回上次玩家的死亡地点,可自定义冷却时间 - 管理员能创建、删除、更新 CDK,玩家可使用有效 CDK 兑换(执行指令)奖励。 +- CDK可以设置使用次数、过期时间、组限制、玩家限制。 ## 指令 | 语法 | 权限 | 说明 | From c2645a55fd9b78c9a5fd51269cff4a50d6092560 Mon Sep 17 00:00:00 2001 From: xien <2383759126@qq.com> Date: Sun, 29 Jun 2025 21:59:03 +0800 Subject: [PATCH 04/20] doc:en --- src/TrCDK/README.en-US.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TrCDK/README.en-US.md b/src/TrCDK/README.en-US.md index 6fbd58d8e..07cb4fc06 100644 --- a/src/TrCDK/README.en-US.md +++ b/src/TrCDK/README.en-US.md @@ -3,6 +3,7 @@ - Source: ARK Server - Can return the player's last death location with customizable cooldown time. - Administrators can create, delete, and update CDKs, and players can redeem (execute commands) rewards using valid CDKs. +- CDKs can set usage times, expiration time, group restrictions, and player restrictions. ## Commands From dfa44f5d3b685cd6a7e4599422fca40f453e9640 Mon Sep 17 00:00:00 2001 From: xien <2383759126@qq.com> Date: Sun, 29 Jun 2025 22:08:22 +0800 Subject: [PATCH 05/20] =?UTF-8?q?fix=EF=BC=9A=E8=A1=A5=E8=8D=AF=E7=81=8C?= =?UTF-8?q?=E6=B3=A8=E6=88=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/TrCDK/Data.cs | 202 ++++++++++++++++++++++++++++++++-------------- src/TrCDK/Main.cs | 1 + 2 files changed, 143 insertions(+), 60 deletions(-) diff --git a/src/TrCDK/Data.cs b/src/TrCDK/Data.cs index 45b24222c..8970e80d5 100644 --- a/src/TrCDK/Data.cs +++ b/src/TrCDK/Data.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using Microsoft.Data.Sqlite; -using static Terraria.ID.ContentSamples.CreativeHelper; namespace TrCDK; @@ -13,81 +12,164 @@ public class CDK public int Usetime { get; set; } public long Utiltime { get; set; } public string Grouplimit { get; set; } = ""; - public string Playerlimit { get; set; } = ""; - public string Used { get; set; } = ""; - public string Cmds { get; set; } = ""; - } + public string Playerlimit { get; set; } = ""; + public string Used { get; set; } = ""; + public string Cmds { get; set; } = ""; +} + internal class Data { public static SqliteConnection? DB; const string path = "tshock/TrCDK.sqlite"; + public static void Init() { DB = new SqliteConnection($"Data Source={path};"); DB.Open(); - Command("create table if not exists Data(CDKname text,Usetime int(32),Utiltime int(64),Grouplimit text,Playerlimit text,Used text, Cmds text)"); - } - public static SqliteDataReader Command(string cmd) - { - return new SqliteCommand(cmd, DB).ExecuteReader(); + + // 使用参数化命令创建表 + using var cmd = new SqliteCommand( + "CREATE TABLE IF NOT EXISTS Data(" + + "CDKname TEXT PRIMARY KEY, " + + "Usetime INTEGER, " + + "Utiltime INTEGER, " + + "Grouplimit TEXT, " + + "Playerlimit TEXT, " + + "Used TEXT, " + + "Cmds TEXT)", DB); + cmd.ExecuteNonQuery(); } + public static bool Insert(string CDKname, int Usetime, long Utiltime, string Grouplimit, string Playerlimit, string Cmds) { - using (var reader = Command($"select * from Data where CDKname='{CDKname}'")) - { - while (reader.Read()) - { - if (reader.GetString(0) == CDKname) - { - return false; - } + if (DB == null) + { + throw new InvalidOperationException(GetString("数据库未初始化")); + } + + // 使用参数化查询检查CDK是否已存在 + using (var checkCmd = new SqliteCommand("SELECT COUNT(*) FROM Data WHERE CDKname = @cdkname", DB)) + { + checkCmd.Parameters.AddWithValue("@cdkname", CDKname); + var count = Convert.ToInt32(checkCmd.ExecuteScalar()); + if (count > 0) + { + return false; // CDK已存在 } - } - Command($"insert into Data(CDKname,Usetime,Utiltime,Grouplimit,Playerlimit,Used,Cmds)values('{CDKname}','{Usetime}','{Utiltime}','{Grouplimit}','{Playerlimit}','','{Cmds}')"); - return true; + } + + // 使用参数化查询插入新CDK + using var insertCmd = new SqliteCommand( + "INSERT INTO Data (CDKname, Usetime, Utiltime, Grouplimit, Playerlimit, Used, Cmds) " + + "VALUES (@cdkname, @usetime, @utiltime, @grouplimit, @playerlimit, @used, @cmds)", DB); + + insertCmd.Parameters.AddWithValue("@cdkname", CDKname); + insertCmd.Parameters.AddWithValue("@usetime", Usetime); + insertCmd.Parameters.AddWithValue("@utiltime", Utiltime); + insertCmd.Parameters.AddWithValue("@grouplimit", Grouplimit); + insertCmd.Parameters.AddWithValue("@playerlimit", Playerlimit); + insertCmd.Parameters.AddWithValue("@used", ""); + insertCmd.Parameters.AddWithValue("@cmds", Cmds); + + insertCmd.ExecuteNonQuery(); + return true; } - public static void Update(string CDKname, int Usetime, long Utiltime, string Grouplimit, string Playerlimit, string Used, string Cmds) - { - using (var reader = Command($"select * from Data where CDKname='{CDKname}'")) - { - while (reader.Read()) - { - if (reader.GetString(0) == CDKname) - { - Command($"UPDATE Data SET Usetime='{Usetime}',Utiltime='{Utiltime}',Grouplimit='{Grouplimit}',Playerlimit='{Playerlimit}',Used='{Used}',Cmds='{Cmds}' WHERE CDKname='{CDKname}'"); - } - } - } - } - public static CDK GetData(string name) + + public static void Update(string CDKname, int Usetime, long Utiltime, string Grouplimit, string Playerlimit, string Used, string Cmds) { - var Res = new CDK(); - using(var reader=Command($"select * from Data where CDKname='{name}'")) + if (DB == null) { - while (reader.Read()) + throw new InvalidOperationException(GetString("数据库未初始化")); + } + + using var updateCmd = new SqliteCommand( + "UPDATE Data SET Usetime = @usetime, Utiltime = @utiltime, Grouplimit = @grouplimit, " + + "Playerlimit = @playerlimit, Used = @used, Cmds = @cmds WHERE CDKname = @cdkname", DB); + + updateCmd.Parameters.AddWithValue("@usetime", Usetime); + updateCmd.Parameters.AddWithValue("@utiltime", Utiltime); + updateCmd.Parameters.AddWithValue("@grouplimit", Grouplimit); + updateCmd.Parameters.AddWithValue("@playerlimit", Playerlimit); + updateCmd.Parameters.AddWithValue("@used", Used); + updateCmd.Parameters.AddWithValue("@cmds", Cmds); + updateCmd.Parameters.AddWithValue("@cdkname", CDKname); + + updateCmd.ExecuteNonQuery(); + } + + public static CDK GetData(string name) + { + if (DB == null) + { + throw new InvalidOperationException(GetString("数据库未初始化")); + } + + var result = new CDK(); + using var cmd = new SqliteCommand("SELECT * FROM Data WHERE CDKname = @name", DB); + cmd.Parameters.AddWithValue("@name", name); + + using var reader = cmd.ExecuteReader(); + if (reader.Read()) + { + result = new CDK() { - Res = new CDK(){ Cdkname = reader.GetString(0), Usetime = reader.GetInt32(1), Utiltime = reader.GetInt64(2), Grouplimit = reader.GetString(3), Playerlimit = reader.GetString(4), Used = reader.GetString(5), Cmds = reader.GetString(6)}; + Cdkname = reader.GetString(0), + Usetime = reader.GetInt32(1), + Utiltime = reader.GetInt64(2), + Grouplimit = reader.GetString(3), + Playerlimit = reader.GetString(4), + Used = reader.GetString(5), + Cmds = reader.GetString(6) + }; + } + return result; + } - } + public static CDK[] GetAllData() + { + if (DB == null) + { + throw new InvalidOperationException(GetString("数据库未初始化")); } - return Res; + + var result = new List(); + using var cmd = new SqliteCommand("SELECT * FROM Data", DB); + using var reader = cmd.ExecuteReader(); + + while (reader.Read()) + { + result.Add(new CDK() + { + Cdkname = reader.GetString(0), + Usetime = reader.GetInt32(1), + Utiltime = reader.GetInt64(2), + Grouplimit = reader.GetString(3), + Playerlimit = reader.GetString(4), + Used = reader.GetString(5), + Cmds = reader.GetString(6) + }); + } + return result.ToArray(); + } + + public static bool DelCDK(string name) + { + if (DB == null) + { + throw new InvalidOperationException(GetString("数据库未初始化")); + } + + using var cmd = new SqliteCommand("DELETE FROM Data WHERE CDKname = @name", DB); + cmd.Parameters.AddWithValue("@name", name); + cmd.ExecuteNonQuery(); + return true; + } + + // 关闭数据库连接的 + public static void Close() + { + DB?.Close(); + DB?.Dispose(); + DB = null; } - public static CDK[] GetAllData() - { - var Res = new List(); - using (var reader = Command("select * from Data")) - { - while (reader.Read()) - { - Res.Add (new CDK() { Cdkname = reader.GetString(0), Usetime = reader.GetInt32(1), Utiltime = reader.GetInt64(2), Grouplimit = reader.GetString(3), Playerlimit = reader.GetString(4), Used = reader.GetString(5), Cmds = reader.GetString(6) }); - - } - } - return Res.ToArray(); - } - public static bool DelCDK(string name) - { - Command($"DELETE FROM Data where CDKname='{name}'"); - return true; - } - } +} \ No newline at end of file diff --git a/src/TrCDK/Main.cs b/src/TrCDK/Main.cs index 2d6500087..1690b6111 100644 --- a/src/TrCDK/Main.cs +++ b/src/TrCDK/Main.cs @@ -31,6 +31,7 @@ protected override void Dispose(bool Disposing) { if (Disposing) { + Data.Close(); Commands.ChatCommands.RemoveAll(c => c.CommandDelegate == this.UseCDK || c.CommandDelegate == this.LoadAllCDK || c.CommandDelegate == this.AddCDKCmd || From 0b8532a4f051931e69b27d7fe395a88951744ea7 Mon Sep 17 00:00:00 2001 From: xien <2383759126@qq.com> Date: Sun, 29 Jun 2025 23:59:41 +0800 Subject: [PATCH 06/20] fix:waring --- src/TrCDK/TSPlayerExtensions.cs | 5 +-- src/TrCDK/TShockCommandExtensions.cs | 5 +-- src/TrCDK/TypeExtensions.cs | 57 +++++++++++++++++----------- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/TrCDK/TSPlayerExtensions.cs b/src/TrCDK/TSPlayerExtensions.cs index 535e18174..e2bec8a35 100644 --- a/src/TrCDK/TSPlayerExtensions.cs +++ b/src/TrCDK/TSPlayerExtensions.cs @@ -4,8 +4,8 @@ using Microsoft.Xna.Framework; using TShockAPI; -namespace TrCDK -{ +namespace TrCDK; + public static class TSPlayerExtensions { public static void SendMessageFormat(this TSPlayer Player, Color Colour, string MessageFormat, params object[] args) @@ -56,4 +56,3 @@ public static bool PermissionlessInvoke(this TSPlayer player, string text, bool return true; } } -} diff --git a/src/TrCDK/TShockCommandExtensions.cs b/src/TrCDK/TShockCommandExtensions.cs index 18d1938f7..40eaace9f 100644 --- a/src/TrCDK/TShockCommandExtensions.cs +++ b/src/TrCDK/TShockCommandExtensions.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using TShockAPI; -namespace TrCDK -{ +namespace TrCDK; + public static class TShockCommandExtensions { public static bool RunWithoutPermissions(this Command cmd, string msg, TSPlayer ply, List parms, bool silent = false) @@ -21,4 +21,3 @@ public static bool RunWithoutPermissions(this Command cmd, string msg, TSPlayer return true; } } -} diff --git a/src/TrCDK/TypeExtensions.cs b/src/TrCDK/TypeExtensions.cs index d4850288e..a3a427e8c 100644 --- a/src/TrCDK/TypeExtensions.cs +++ b/src/TrCDK/TypeExtensions.cs @@ -1,27 +1,40 @@ using System; using System.Reflection; -namespace TrCDK +namespace TrCDK; + +public static class TypeExtensions { - public static class TypeExtensions - { - public static T CallPrivateMethod(this Type _type, bool StaticMember, string Name, params object[] Params) - { - var bindingFlags = BindingFlags.NonPublic; - bindingFlags |= (StaticMember ? BindingFlags.Static : BindingFlags.Instance); - var method = _type.GetMethod(Name, bindingFlags); - return (T)method.Invoke(StaticMember ? null : _type, Params); - } + public static T CallPrivateMethod(this Type _type, bool StaticMember, string Name, params object[] Params) + { + var bindingFlags = BindingFlags.NonPublic; + bindingFlags |= StaticMember ? BindingFlags.Static : BindingFlags.Instance; + var method = _type.GetMethod(Name, bindingFlags) ?? throw new InvalidOperationException(GetString($"Ҳ: {Name}")); + var result = method.Invoke(StaticMember ? null : _type, Params); + + return result == null + ? (typeof(T).IsValueType && Nullable.GetUnderlyingType(typeof(T)) == null + ? default! + : (T) (object) null!) + : (T) result; + } + + public static T GetPrivateField(this Type type, object Instance, string Name, params object[] Param) + { + var bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; + var field = type.GetField(Name, bindingAttr); + + if (field == null) + { + return default!; + } + + var value = field.GetValue(Instance); - public static T GetPrivateField(this Type type, object Instance, string Name, params object[] Param) - { - var bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; - var field = type.GetField(Name, bindingAttr); - if (field == null) - { - return default(T); - } - return (T)field.GetValue(Instance); - } - } -} + return value == null + ? (typeof(T).IsValueType && Nullable.GetUnderlyingType(typeof(T)) == null + ? default! + : (T) (object) null!) + : (T) value; + } +} \ No newline at end of file From f11d46df60f690a3722f5b5f2762feb8bbe9e1af Mon Sep 17 00:00:00 2001 From: Controllerdestiny <523321293@qq.com> Date: Mon, 30 Jun 2025 13:33:31 +0800 Subject: [PATCH 07/20] =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/LazyAPI/Extensions/TSPlayerExtension.cs | 21 ++ src/TrCDK/Command.cs | 147 ++++++++++++ src/TrCDK/Data.cs | 189 ++++----------- src/TrCDK/Main.cs | 242 +------------------- src/TrCDK/TSPlayerExtensions.cs | 58 ----- src/TrCDK/TShockCommandExtensions.cs | 23 -- src/TrCDK/TrCDK.csproj | 4 + src/TrCDK/TypeExtensions.cs | 40 ---- 8 files changed, 224 insertions(+), 500 deletions(-) create mode 100644 src/TrCDK/Command.cs delete mode 100644 src/TrCDK/TSPlayerExtensions.cs delete mode 100644 src/TrCDK/TShockCommandExtensions.cs delete mode 100644 src/TrCDK/TypeExtensions.cs diff --git a/src/LazyAPI/Extensions/TSPlayerExtension.cs b/src/LazyAPI/Extensions/TSPlayerExtension.cs index 548e5de4b..33b5d77cb 100644 --- a/src/LazyAPI/Extensions/TSPlayerExtension.cs +++ b/src/LazyAPI/Extensions/TSPlayerExtension.cs @@ -26,6 +26,27 @@ public static void SendPlayerSlot(this TSPlayer player, int slot) player.SendData(PacketTypes.PlayerSlot, "", slot); } + public static void ExecCommand(this TSPlayer player, string cmd) + { + try + { + player.tempGroup = new SuperAdminGroup(); + TShockAPI.Commands.HandleCommand(player, cmd.SFormat(player.Name)); + } + finally + { + player.tempGroup = null; + } + } + + public static void ExecCommand(this TSPlayer player, IEnumerable cmds) + { + foreach (var cmd in cmds) + { + player.ExecCommand(cmd); + } + } + public static Dictionary GetProgress(this TSPlayer Player) { return GameProgress.DefaultProgressNames diff --git a/src/TrCDK/Command.cs b/src/TrCDK/Command.cs new file mode 100644 index 000000000..b9092d827 --- /dev/null +++ b/src/TrCDK/Command.cs @@ -0,0 +1,147 @@ +using LazyAPI.Attributes; +using LazyAPI.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TShockAPI; + +namespace TrCDK; + +[Command("cdk")] +[Permissions("cdk.use")] +public class Command +{ + [Alias("give")] + [Permissions("cdk.give")] + public static void Give(CommandArgs args, TSPlayer? player, string cmd) + { + if(player == null) + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:玩家不存在]")); + return; + } + var cmds = cmd.Split(','); + if (cmds.Length > 0) + { + args.Player.ExecCommand(cmds); + args.Player.SendSuccessMessage(GetString($"[c/00FF00:【CDK管理】]\n[c/ffd700:已给予玩家 {player.Name} CDK奖励]")); + } + else + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:指令不能为空]")); + } + } + + [Alias("update")] + [Permissions("cdk.update")] + public static void Update(CommandArgs args, string cdkname, int usetime, long utiltime, string grouplimit = "", string playerlimit = "", string cmds = "") + { + var cdkData = CDK.GetData(cdkname); + if (cdkData == null) + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:CDK不存在]")); + return; + } + CDK.Update(cdkname, usetime, utiltime, grouplimit, playerlimit, cdkData.Used, cmds); + args.Player.SendSuccessMessage(GetString("[c/00FF00:【CDK】]\n[c/ffd700:CDK更新成功]")); + } + + [Alias("del")] + [Permissions("cdk.del")] + public static void Delete(CommandArgs args, string cdkname) + { + if (CDK.GetData(cdkname) == null) + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:CDK不存在]")); + return; + } + CDK.DelCDK(cdkname); + args.Player.SendSuccessMessage(GetString("[c/00FF00:【CDK】]\n[c/ffd700:CDK删除成功]")); + } + + [Alias("add")] + [Permissions("cdk.add")] + public static void Add(CommandArgs args, string cdkname, int usetime, long utiltime, string grouplimit = "", string playerlimit = "", string cmds = "") + { + if (CDK.Insert(cdkname, usetime, utiltime, grouplimit, playerlimit, cmds)) + { + args.Player.SendSuccessMessage(GetString("[c/00FF00:【CDK】]\n[c/ffd700:CDK添加成功]")); + } + else + { + args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:CDK添加失败]")); + } + } + + [Alias("loadall")] + [Permissions("cdk.loadall")] + public static void LoadAll(CommandArgs args) + { + var cdkList = CDK.GetAllData(); + if (cdkList.Length == 0) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:没有可用的CDK]")); + return; + } + var sb = new StringBuilder(); + sb.AppendLine(GetString("[c/00FF00:【CDK】]\n[c/ffd700:当前可用的CDK列表]")); + foreach (var cdk in cdkList) + { + sb.AppendLine(GetString($"[c/ffd700:{cdk.Name}] [c/00FF00:剩余使用次数: {cdk.Usetime}] [c/ffd700:过期时间: {new DateTime(cdk.Utiltime).ToLocalTime()}]")); + } + args.Player.SendInfoMessage(sb.ToString().Trim()); + } + + [Main] + [RealPlayer] + public static void Use(CommandArgs args, string cdk) + { + var cdkData = CDK.GetData(cdk); + if (cdkData == null) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:CDK不存在或已失效]")); + return; + } + + if (cdkData.Playerlimit != "") + { + if (!cdkData.Playerlimit.Contains(args.Player.Name)) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你不在该CDK的领取名单中]")); + return; + } + } + if (cdkData.Grouplimit != "") + { + if (!cdkData.Grouplimit.Contains(args.Player.Group.Name)) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你所在的组不能领取该CDK]")); + return; + } + } + if (cdkData.Usetime < 1) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你手慢了, 该CDK已经被领完]")); + return; + } + var time2 = Convert.ToDateTime(DateTime.Now.ToString()).Ticks; + var min = (cdkData.Utiltime - time2) / 10000000; + if (min <= 0) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你来晚了, 该CDK已经过期了]")); + return; + } + if (cdkData.Used.Contains(args.Player.Name)) + { + args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:已经领取过了,不能太贪心哦]")); + return; + } + var strcmd = cdkData.Cmds.Replace("[plr]", args.Player.Name); + var cmds = strcmd.Split(','); + args.Player.ExecCommand(cmds); + CDK.Update(cdkData.Name, cdkData.Usetime - 1, cdkData.Utiltime, cdkData.Grouplimit, cdkData.Playerlimit, (cdkData.Used == "") ? args.Player.Name : cdkData.Used + "," + args.Player.Name, cdkData.Cmds); + args.Player.SendSuccessMessage(GetString("[c/00FF00:【CDK】]\n[c/ffd700:CDK兑换成功!]")); + } +} diff --git a/src/TrCDK/Data.cs b/src/TrCDK/Data.cs index 8970e80d5..5d25903d6 100644 --- a/src/TrCDK/Data.cs +++ b/src/TrCDK/Data.cs @@ -1,175 +1,84 @@ -using System.Data; -using System; -using System.Collections.Generic; -using System.IO; -using Microsoft.Data.Sqlite; +using Microsoft.Data.Sqlite; +using LinqToDB.Mapping; +using LazyAPI.Database; +using LinqToDB; namespace TrCDK; -public class CDK +[Table("CDK")] +public class CDK : RecordBase { - public string Cdkname { get; set; } = ""; + [Column("name"), PrimaryKey, NotNull] + public string Name { get; set; } = string.Empty; + + [Column("usetime")] public int Usetime { get; set; } + + [Column("utiltime")] public long Utiltime { get; set; } - public string Grouplimit { get; set; } = ""; - public string Playerlimit { get; set; } = ""; - public string Used { get; set; } = ""; - public string Cmds { get; set; } = ""; -} -internal class Data -{ - public static SqliteConnection? DB; - const string path = "tshock/TrCDK.sqlite"; + [Column("grouplimit")] + public string Grouplimit { get; set; } = string.Empty; - public static void Init() - { - DB = new SqliteConnection($"Data Source={path};"); - DB.Open(); + [Column("playerlimit")] + public string Playerlimit { get; set; } = string.Empty; - // 使用参数化命令创建表 - using var cmd = new SqliteCommand( - "CREATE TABLE IF NOT EXISTS Data(" + - "CDKname TEXT PRIMARY KEY, " + - "Usetime INTEGER, " + - "Utiltime INTEGER, " + - "Grouplimit TEXT, " + - "Playerlimit TEXT, " + - "Used TEXT, " + - "Cmds TEXT)", DB); - cmd.ExecuteNonQuery(); - } + [Column("used")] + public string Used { get; set; } = string.Empty; + + [Column("cmds")] + public string Cmds { get; set; } = string.Empty; + + private static readonly Context _context = Db.Context("CDK"); public static bool Insert(string CDKname, int Usetime, long Utiltime, string Grouplimit, string Playerlimit, string Cmds) { - if (DB == null) + _context.Records.Append(new CDK { - throw new InvalidOperationException(GetString("数据库未初始化")); - } - - // 使用参数化查询检查CDK是否已存在 - using (var checkCmd = new SqliteCommand("SELECT COUNT(*) FROM Data WHERE CDKname = @cdkname", DB)) - { - checkCmd.Parameters.AddWithValue("@cdkname", CDKname); - var count = Convert.ToInt32(checkCmd.ExecuteScalar()); - if (count > 0) - { - return false; // CDK已存在 - } - } - - // 使用参数化查询插入新CDK - using var insertCmd = new SqliteCommand( - "INSERT INTO Data (CDKname, Usetime, Utiltime, Grouplimit, Playerlimit, Used, Cmds) " + - "VALUES (@cdkname, @usetime, @utiltime, @grouplimit, @playerlimit, @used, @cmds)", DB); - - insertCmd.Parameters.AddWithValue("@cdkname", CDKname); - insertCmd.Parameters.AddWithValue("@usetime", Usetime); - insertCmd.Parameters.AddWithValue("@utiltime", Utiltime); - insertCmd.Parameters.AddWithValue("@grouplimit", Grouplimit); - insertCmd.Parameters.AddWithValue("@playerlimit", Playerlimit); - insertCmd.Parameters.AddWithValue("@used", ""); - insertCmd.Parameters.AddWithValue("@cmds", Cmds); - - insertCmd.ExecuteNonQuery(); + Name = CDKname, + Usetime = Usetime, + Utiltime = Utiltime, + Grouplimit = Grouplimit, + Playerlimit = Playerlimit, + Used = "", + Cmds = Cmds + }); return true; } public static void Update(string CDKname, int Usetime, long Utiltime, string Grouplimit, string Playerlimit, string Used, string Cmds) { - if (DB == null) + var cdk = _context.Records.FirstOrDefault(c => c.Name == CDKname); + if (cdk != null) { - throw new InvalidOperationException(GetString("数据库未初始化")); + cdk.Usetime = Usetime; + cdk.Utiltime = Utiltime; + cdk.Grouplimit = Grouplimit; + cdk.Playerlimit = Playerlimit; + cdk.Used = Used; + cdk.Cmds = Cmds; + _context.Update(cdk); } - - using var updateCmd = new SqliteCommand( - "UPDATE Data SET Usetime = @usetime, Utiltime = @utiltime, Grouplimit = @grouplimit, " + - "Playerlimit = @playerlimit, Used = @used, Cmds = @cmds WHERE CDKname = @cdkname", DB); - - updateCmd.Parameters.AddWithValue("@usetime", Usetime); - updateCmd.Parameters.AddWithValue("@utiltime", Utiltime); - updateCmd.Parameters.AddWithValue("@grouplimit", Grouplimit); - updateCmd.Parameters.AddWithValue("@playerlimit", Playerlimit); - updateCmd.Parameters.AddWithValue("@used", Used); - updateCmd.Parameters.AddWithValue("@cmds", Cmds); - updateCmd.Parameters.AddWithValue("@cdkname", CDKname); - - updateCmd.ExecuteNonQuery(); } - public static CDK GetData(string name) + public static CDK? GetData(string name) { - if (DB == null) - { - throw new InvalidOperationException(GetString("数据库未初始化")); - } - - var result = new CDK(); - using var cmd = new SqliteCommand("SELECT * FROM Data WHERE CDKname = @name", DB); - cmd.Parameters.AddWithValue("@name", name); - - using var reader = cmd.ExecuteReader(); - if (reader.Read()) - { - result = new CDK() - { - Cdkname = reader.GetString(0), - Usetime = reader.GetInt32(1), - Utiltime = reader.GetInt64(2), - Grouplimit = reader.GetString(3), - Playerlimit = reader.GetString(4), - Used = reader.GetString(5), - Cmds = reader.GetString(6) - }; - } - return result; + return _context.Records.FirstOrDefault(c => c.Name == name); } public static CDK[] GetAllData() { - if (DB == null) - { - throw new InvalidOperationException(GetString("数据库未初始化")); - } - - var result = new List(); - using var cmd = new SqliteCommand("SELECT * FROM Data", DB); - using var reader = cmd.ExecuteReader(); - - while (reader.Read()) - { - result.Add(new CDK() - { - Cdkname = reader.GetString(0), - Usetime = reader.GetInt32(1), - Utiltime = reader.GetInt64(2), - Grouplimit = reader.GetString(3), - Playerlimit = reader.GetString(4), - Used = reader.GetString(5), - Cmds = reader.GetString(6) - }); - } - return result.ToArray(); + return [.. _context.Records]; } public static bool DelCDK(string name) { - if (DB == null) + var cdk = _context.Records.FirstOrDefault(c => c.Name == name); + if (cdk != null) { - throw new InvalidOperationException(GetString("数据库未初始化")); + _context.Delete(cdk); + return true; } - - using var cmd = new SqliteCommand("DELETE FROM Data WHERE CDKname = @name", DB); - cmd.Parameters.AddWithValue("@name", name); - cmd.ExecuteNonQuery(); - return true; - } - - // 关闭数据库连接的 - public static void Close() - { - DB?.Close(); - DB?.Dispose(); - DB = null; + return false; } } \ No newline at end of file diff --git a/src/TrCDK/Main.cs b/src/TrCDK/Main.cs index 1690b6111..d27ea7958 100644 --- a/src/TrCDK/Main.cs +++ b/src/TrCDK/Main.cs @@ -3,6 +3,7 @@ using TerrariaApi.Server; using System.Globalization; using Microsoft.Xna.Framework; +using LazyAPI.Extensions; namespace TrCDK; @@ -17,248 +18,11 @@ public TrCDK(Main game) : base(game) { } public override void Initialize() { - Data.Init(); - // 添加所有CDK相关指令 - Commands.ChatCommands.Add(new Command("cdk.use", this.UseCDK, "cdk")); - Commands.ChatCommands.Add(new Command("cdk.admin.loadall", this.LoadAllCDK, "cdkloadall")); - Commands.ChatCommands.Add(new Command("cdk.admin.add", this.AddCDKCmd, "cdkadd")); - Commands.ChatCommands.Add(new Command("cdk.admin.del", this.DelCDKCmd, "cdkdel")); - Commands.ChatCommands.Add(new Command("cdk.admin.update", this.UpdateCDKCmd, "cdkupdate")); - Commands.ChatCommands.Add(new Command("cdk.admin.give", this.GiveCDKCmd, "cdkgive")); + } protected override void Dispose(bool Disposing) { - if (Disposing) - { - Data.Close(); - Commands.ChatCommands.RemoveAll(c => c.CommandDelegate == this.UseCDK || - c.CommandDelegate == this.LoadAllCDK || - c.CommandDelegate == this.AddCDKCmd || - c.CommandDelegate == this.DelCDKCmd || - c.CommandDelegate == this.UpdateCDKCmd || - c.CommandDelegate == this.GiveCDKCmd); - } - base.Dispose(Disposing); - } - - // 添加CDK指令 - private void AddCDKCmd(CommandArgs args) - { - if (args.Parameters.Count < 6) - { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:用法: /cdkadd <使用次数> <过期时间> <组限制> <玩家限制> <指令>]")); - args.Player.SendInfoMessage(GetString("[c/ffd700:过期时间格式: yyyy-MM-ddThh:mm (例: 2025-12-31T23:59)]")); - args.Player.SendInfoMessage(GetString("[c/ffd700:组限制和玩家限制可以用逗号分隔多个,不限制则填 none]")); - return; - } - - var cdkName = args.Parameters[0]; - if (!int.TryParse(args.Parameters[1], out var useTime)) - { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:使用次数必须是数字!]")); - return; - } - - var utilTime = args.Parameters[2]; - var groupLimit = args.Parameters[3] == "none" ? "" : args.Parameters[3]; - var playerLimit = args.Parameters[4] == "none" ? "" : args.Parameters[4]; - - // 组合剩余参数作为指令 - var cmds = string.Join(" ", args.Parameters.Skip(5)); - - try - { - var timeForInfo = new DateTimeFormatInfo - { - ShortDatePattern = "yyyy-MM-ddThh:mm" - }; - var time = Convert.ToDateTime(utilTime, timeForInfo); - - if (Data.Insert(cdkName, useTime, Convert.ToDateTime(time.ToString()).Ticks, groupLimit, playerLimit, cmds)) - { - args.Player.SendSuccessMessage(GetString($"[c/00FF00:【CDK管理】]\n[c/ffd700:成功添加CDK: {cdkName}]")); - } - else - { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:添加CDK失败,可能已存在同名CDK]")); - } - } - catch (Exception ex) - { - args.Player.SendErrorMessage(GetString($"[c/FF0000:【CDK管理】]\n[c/ffd700:时间格式错误: {ex.Message}]")); - } - } - - // 删除CDK指令 - private void DelCDKCmd(CommandArgs args) - { - if (args.Parameters.Count < 1) - { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:用法: /cdkdel ]")); - return; - } - - var cdkName = args.Parameters[0]; - Data.DelCDK(cdkName); - args.Player.SendSuccessMessage(GetString($"[c/00FF00:【CDK管理】]\n[c/ffd700:已删除CDK: {cdkName}]")); - } - - // 更新CDK指令 - private void UpdateCDKCmd(CommandArgs args) - { - if (args.Parameters.Count < 7) - { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:用法: /cdkupdate <使用次数> <过期时间> <组限制> <玩家限制> <已使用玩家> <指令>]")); - args.Player.SendInfoMessage(GetString("[c/ffd700:过期时间格式: yyyy-MM-ddThh:mm]")); - args.Player.SendInfoMessage(GetString("[c/ffd700:不需要修改的项目可以填 none]")); - return; - } - - var cdkName = args.Parameters[0]; - if (!int.TryParse(args.Parameters[1], out var useTime)) - { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:使用次数必须是数字!]")); - return; - } - - var utilTime = args.Parameters[2]; - var groupLimit = args.Parameters[3] == "none" ? "" : args.Parameters[3]; - var playerLimit = args.Parameters[4] == "none" ? "" : args.Parameters[4]; - var used = args.Parameters[5] == "none" ? "" : args.Parameters[5]; - var cmds = string.Join(" ", args.Parameters.Skip(6)); - - try - { - var timeForInfo = new DateTimeFormatInfo - { - ShortDatePattern = "yyyy-MM-ddThh:mm" - }; - var time = Convert.ToDateTime(utilTime, timeForInfo); - - Data.Update(cdkName, useTime, Convert.ToDateTime(time.ToString()).Ticks, groupLimit, playerLimit, used, cmds); - args.Player.SendSuccessMessage(GetString($"[c/00FF00:【CDK管理】]\n[c/ffd700:成功更新CDK: {cdkName}]")); - } - catch (Exception ex) - { - args.Player.SendErrorMessage(GetString($"[c/FF0000:【CDK管理】]\n[c/ffd700:时间格式错误: {ex.Message}]")); - } - } - - // 加载所有CDK指令 - private void LoadAllCDK(CommandArgs args) - { - var allcdk = Data.GetAllData(); - args.Player.SendInfoMessage(GetString("[c/00FF00:【CDK管理】]\n[c/ffd700:=== 所有CDK列表 ===]")); - - foreach (var cdk in allcdk) - { - var cdkObj = (CDK) cdk; - var expireTime = new DateTime(cdkObj.Utiltime); - args.Player.SendInfoMessage(GetString($"[c/ffd700:CDK: {cdkObj.Cdkname} | 剩余次数: {cdkObj.Usetime} | 过期时间: {expireTime:yyyy-MM-dd HH:mm}]")); - } - } - - // 给予CDK奖励指令 - private void GiveCDKCmd(CommandArgs args) - { - if (args.Parameters.Count < 2) - { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:用法: /cdkgive <玩家名> <指令列表>]")); - args.Player.SendInfoMessage(GetString("[c/ffd700:指令列表用逗号分隔,例: give [plr] 1 1,heal [plr]]")); - return; - } - - var playerName = args.Parameters[0]; - var cmds = string.Join(" ", args.Parameters.Skip(1)); - - var players = TShockAPI.TSPlayer.FindByNameOrID(playerName); - if (players.Count == 0) - { - args.Player.SendErrorMessage(GetString($"[c/FF0000:【CDK管理】]\n[c/ffd700:找不到玩家: {playerName}]")); - return; - } - - if (players.Count > 1) - { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:找到多个匹配的玩家,请使用更精确的名称]")); - return; - } - - var targetPlayer = players[0]; - if (cmds != "") - { - var cmdArray = cmds.Split(','); - foreach (var cmd in cmdArray) - { - var processedCmd = cmd.Replace("[plr]", targetPlayer.Name).Trim(); - targetPlayer.PermissionlessInvoke(processedCmd); - } - args.Player.SendSuccessMessage(GetString($"[c/00FF00:【CDK管理】]\n[c/ffd700:已给予玩家 {targetPlayer.Name} CDK奖励]")); - } - else - { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:指令不能为空]")); - } - } - - // 使用CDK指令(玩家用) - private void UseCDK(CommandArgs args) - { - if (args.Parameters.Count == 0) - { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:指令:/cdk CDK兑换码 - 兑换一个CDK礼包]")); - } - else - { - var cdk1 = Data.GetData(args.Parameters[0]); - if (cdk1 == null) - { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:CDK不存在或已失效]")); - return; - } - - if (cdk1.Playerlimit != "") - { - if (!cdk1.Playerlimit.Contains(args.Player.Name)) - { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你不在该CDK的领取名单中]")); - return; - } - } - if (cdk1.Grouplimit != "") - { - if (!cdk1.Grouplimit.Contains(args.Player.Group.Name)) - { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你所在的组不能领取该CDK]")); - return; - } - } - if (cdk1.Usetime < 1) - { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你手慢了, 该CDK已经被领完]")); - return; - } - var time2 = Convert.ToDateTime(DateTime.Now.ToString()).Ticks; - var min = (cdk1.Utiltime - time2) / 10000000; - if (min <= 0) - { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你来晚了, 该CDK已经过期了]")); - return; - } - if (cdk1.Used.Contains(args.Player.Name)) - { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:已经领取过了,不能太贪心哦]")); - return; - } - var strcmd = cdk1.Cmds.Replace("[plr]", args.Player.Name); - var cmds = strcmd.Split(','); - foreach (var cmd in cmds) - { - args.Player.PermissionlessInvoke(cmd); - } - Data.Update(cdk1.Cdkname, cdk1.Usetime - 1, cdk1.Utiltime, cdk1.Grouplimit, cdk1.Playerlimit, (cdk1.Used == "") ? args.Player.Name : cdk1.Used + "," + args.Player.Name, cdk1.Cmds); - args.Player.SendSuccessMessage(GetString("[c/00FF00:【CDK】]\n[c/ffd700:CDK兑换成功!]")); - } + } } \ No newline at end of file diff --git a/src/TrCDK/TSPlayerExtensions.cs b/src/TrCDK/TSPlayerExtensions.cs deleted file mode 100644 index e2bec8a35..000000000 --- a/src/TrCDK/TSPlayerExtensions.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Xna.Framework; -using TShockAPI; - -namespace TrCDK; - - public static class TSPlayerExtensions - { - public static void SendMessageFormat(this TSPlayer Player, Color Colour, string MessageFormat, params object[] args) - { - Player.SendMessage(string.Format(MessageFormat, args), Colour); - } - - public static bool PermissionlessInvoke(this TSPlayer player, string text, bool silent = false) - { - if (string.IsNullOrEmpty(text)) - { - return false; - } - var text2 = text.Remove(0, 1); - var list = typeof(Commands).CallPrivateMethod>(StaticMember: true, "ParseParameters", new object[1] { text2 }); - if (list.Count < 1) - { - return false; - } - var cmdName = list[0].ToLower(); - list.RemoveAt(0); - var enumerable = Commands.ChatCommands.Where((Command c) => c.HasAlias(cmdName)); - if (enumerable.Count() == 0) - { - if (player.AwaitingResponse.ContainsKey(cmdName)) - { - Action action = player.AwaitingResponse[cmdName]; - player.AwaitingResponse.Remove(cmdName); - action(new CommandArgs(text2, player, list)); - return true; - } - player.SendErrorMessage(GetString("Ч./helpȡЧб.")); - return true; - } - foreach (var item in enumerable) - { - if (!item.AllowServer && !player.RealPlayer) - { - player.SendErrorMessage(GetString("Ϸʹ.")); - continue; - } - if (item.DoLog && !silent) - { - TShock.Utils.SendLogs(player.Name + GetString(" ִ: /") + text2 + ".", Color.Red); - } - item.RunWithoutPermissions(text2, player, list); - } - return true; - } - } diff --git a/src/TrCDK/TShockCommandExtensions.cs b/src/TrCDK/TShockCommandExtensions.cs deleted file mode 100644 index 40eaace9f..000000000 --- a/src/TrCDK/TShockCommandExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using TShockAPI; - -namespace TrCDK; - - public static class TShockCommandExtensions - { - public static bool RunWithoutPermissions(this Command cmd, string msg, TSPlayer ply, List parms, bool silent = false) - { - try - { - var commandDelegate = cmd.CommandDelegate; - commandDelegate(new CommandArgs(msg, silent, ply, parms)); - } - catch (Exception ex) - { - ply.SendErrorMessage(GetString("ִָʧܣϵԱ")); - TShock.Log.Error(ex.ToString()); - } - return true; - } - } diff --git a/src/TrCDK/TrCDK.csproj b/src/TrCDK/TrCDK.csproj index 1bebdd9fc..1005d2601 100644 --- a/src/TrCDK/TrCDK.csproj +++ b/src/TrCDK/TrCDK.csproj @@ -2,4 +2,8 @@ + + + + \ No newline at end of file diff --git a/src/TrCDK/TypeExtensions.cs b/src/TrCDK/TypeExtensions.cs deleted file mode 100644 index a3a427e8c..000000000 --- a/src/TrCDK/TypeExtensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Reflection; - -namespace TrCDK; - -public static class TypeExtensions -{ - public static T CallPrivateMethod(this Type _type, bool StaticMember, string Name, params object[] Params) - { - var bindingFlags = BindingFlags.NonPublic; - bindingFlags |= StaticMember ? BindingFlags.Static : BindingFlags.Instance; - var method = _type.GetMethod(Name, bindingFlags) ?? throw new InvalidOperationException(GetString($"Ҳ: {Name}")); - var result = method.Invoke(StaticMember ? null : _type, Params); - - return result == null - ? (typeof(T).IsValueType && Nullable.GetUnderlyingType(typeof(T)) == null - ? default! - : (T) (object) null!) - : (T) result; - } - - public static T GetPrivateField(this Type type, object Instance, string Name, params object[] Param) - { - var bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; - var field = type.GetField(Name, bindingAttr); - - if (field == null) - { - return default!; - } - - var value = field.GetValue(Instance); - - return value == null - ? (typeof(T).IsValueType && Nullable.GetUnderlyingType(typeof(T)) == null - ? default! - : (T) (object) null!) - : (T) value; - } -} \ No newline at end of file From 74ed18357f05f7de7db713cfc035467b7eecdd2b Mon Sep 17 00:00:00 2001 From: Controllerdestiny <523321293@qq.com> Date: Mon, 30 Jun 2025 15:56:42 +0800 Subject: [PATCH 08/20] =?UTF-8?q?LazyAPI=E5=91=BD=E4=BB=A4=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=94=AF=E6=8C=81=E5=8F=AF=E9=80=89=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/LazyAPI/Commands/FlexibleCommand.cs | 40 +++++++++++---- src/LazyAPI/PluginContainer.cs | 2 +- src/TrCDK/Command.cs | 67 ++++++++++++++++--------- src/TrCDK/Data.cs | 5 +- src/TrCDK/Main.cs | 3 +- 5 files changed, 79 insertions(+), 38 deletions(-) diff --git a/src/LazyAPI/Commands/FlexibleCommand.cs b/src/LazyAPI/Commands/FlexibleCommand.cs index 9d7a1a967..51574511e 100644 --- a/src/LazyAPI/Commands/FlexibleCommand.cs +++ b/src/LazyAPI/Commands/FlexibleCommand.cs @@ -1,32 +1,38 @@ using MonoMod.Utils; -using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Text; -using System.Threading.Tasks; using TShockAPI; namespace LazyAPI.Commands; internal class FlexibleCommand : CommandBase { private readonly CommandParser.Parser[] argParsers; + private readonly (CommandParser.Parser, object?)[] defaultParsers; private readonly FastReflectionHelper.FastInvoker method; public FlexibleCommand(MethodInfo method, string infoPrefix) : base(method) { var param = method.GetParameters(); var ap = new List(); + var dp = new List<(CommandParser.Parser, object?)>(); var sb = new StringBuilder(); sb.Append(infoPrefix); foreach (var p in param.Skip(1)) { - ap.Add(CommandParser.GetParser(p.ParameterType)); - sb.Append($"<{p.Name}: {CommandParser.GetFriendlyName(p.ParameterType)}> "); + if (p.IsOptional) + { + dp.Add((CommandParser.GetParser(p.ParameterType), p.DefaultValue)); + sb.Append(@$"[{p.Name}:{CommandParser.GetFriendlyName(p.ParameterType)}] "); + } + else + { + ap.Add(CommandParser.GetParser(p.ParameterType)); + sb.Append($"<{p.Name}:{CommandParser.GetFriendlyName(p.ParameterType)}> "); + } } - this.argParsers = [.. ap]; + this.defaultParsers = [.. dp]; this.info = sb.ToString(); this.method = method.GetFastInvoker(); } @@ -35,19 +41,35 @@ public override ParseResult TryParse(CommandArgs args, int current) { var p = args.Parameters; var n = this.argParsers.Length; + var d = this.defaultParsers.Length; if (p.Count < n + current) { return this.GetResult(Math.Abs(n + current - p.Count)); } - var a = new object?[n + 1]; + var a = new object?[n + d + 1]; a[0] = args; var unmatched = this.argParsers.Where((t, i) => !t(p[current + i], out a[i + 1])).Count(); if (unmatched != 0) { return this.GetResult(unmatched); } - + if (this.defaultParsers.Where((t, i) => + { + var (k, v) = t; + if (p.Count <= n + i + 1) + { + a[n + i + 1] = v; + return false; + } + else + { + return !k(p[current + n + i], out a[n + i + 1]); + } + }).Any()) + { + return this.GetResult(0); + } if (this.CheckPlayer(args.Player)) { this.method(null, a); diff --git a/src/LazyAPI/PluginContainer.cs b/src/LazyAPI/PluginContainer.cs index aa8c8b9d2..8e26889a1 100644 --- a/src/LazyAPI/PluginContainer.cs +++ b/src/LazyAPI/PluginContainer.cs @@ -10,7 +10,7 @@ public class PluginContainer : LazyPlugin { public override string Author => "cc004 & members of UnrealMultiple"; - public override Version Version => new Version(1, 0, 2, 1); + public override Version Version => new Version(1, 0, 2, 2); public PluginContainer(Main game) : base(game) { } public override void Initialize() diff --git a/src/TrCDK/Command.cs b/src/TrCDK/Command.cs index b9092d827..24afe175a 100644 --- a/src/TrCDK/Command.cs +++ b/src/TrCDK/Command.cs @@ -19,33 +19,34 @@ public static void Give(CommandArgs args, TSPlayer? player, string cmd) { if(player == null) { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:玩家不存在]")); + args.Player.SendErrorMessage(GetString("[c/ffd700:玩家不存在]")); return; } var cmds = cmd.Split(','); if (cmds.Length > 0) { args.Player.ExecCommand(cmds); - args.Player.SendSuccessMessage(GetString($"[c/00FF00:【CDK管理】]\n[c/ffd700:已给予玩家 {player.Name} CDK奖励]")); + args.Player.SendSuccessMessage(GetString($"[c/ffd700:已给予玩家 {player.Name} CDK奖励]")); } else { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK管理】]\n[c/ffd700:指令不能为空]")); + args.Player.SendErrorMessage(GetString("[c/ffd700:指令不能为空]")); } } [Alias("update")] + [Flexible] [Permissions("cdk.update")] public static void Update(CommandArgs args, string cdkname, int usetime, long utiltime, string grouplimit = "", string playerlimit = "", string cmds = "") { var cdkData = CDK.GetData(cdkname); if (cdkData == null) { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:CDK不存在]")); + args.Player.SendErrorMessage(GetString("[c/ffd700:CDK不存在]")); return; } CDK.Update(cdkname, usetime, utiltime, grouplimit, playerlimit, cdkData.Used, cmds); - args.Player.SendSuccessMessage(GetString("[c/00FF00:【CDK】]\n[c/ffd700:CDK更新成功]")); + args.Player.SendSuccessMessage(GetString("[c/ffd700:CDK更新成功]")); } [Alias("del")] @@ -54,54 +55,72 @@ public static void Delete(CommandArgs args, string cdkname) { if (CDK.GetData(cdkname) == null) { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:CDK不存在]")); + args.Player.SendErrorMessage(GetString("[c/ffd700:CDK不存在]")); return; } CDK.DelCDK(cdkname); - args.Player.SendSuccessMessage(GetString("[c/00FF00:【CDK】]\n[c/ffd700:CDK删除成功]")); + args.Player.SendSuccessMessage(GetString("[c/ffd700:CDK删除成功]")); } [Alias("add")] + [Flexible] [Permissions("cdk.add")] - public static void Add(CommandArgs args, string cdkname, int usetime, long utiltime, string grouplimit = "", string playerlimit = "", string cmds = "") + public static void Add(CommandArgs args, string cdkname, int usetime, long utiltime, string cmds, string grouplimit = "", string playerlimit = "") { if (CDK.Insert(cdkname, usetime, utiltime, grouplimit, playerlimit, cmds)) { - args.Player.SendSuccessMessage(GetString("[c/00FF00:【CDK】]\n[c/ffd700:CDK添加成功]")); + args.Player.SendSuccessMessage(GetString("[c/ffd700:CDK添加成功]")); } else { - args.Player.SendErrorMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:CDK添加失败]")); + args.Player.SendErrorMessage(GetString("[c/ffd700:CDK添加失败]")); } } - [Alias("loadall")] - [Permissions("cdk.loadall")] - public static void LoadAll(CommandArgs args) + [Alias("list")] + [Permissions("cdk.list")] + public static void List(CommandArgs args) { var cdkList = CDK.GetAllData(); if (cdkList.Length == 0) { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:没有可用的CDK]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:没有可用的CDK]")); return; } var sb = new StringBuilder(); - sb.AppendLine(GetString("[c/00FF00:【CDK】]\n[c/ffd700:当前可用的CDK列表]")); + sb.AppendLine(GetString("[c/ffd700:当前可用的CDK列表]")); foreach (var cdk in cdkList) { - sb.AppendLine(GetString($"[c/ffd700:{cdk.Name}] [c/00FF00:剩余使用次数: {cdk.Usetime}] [c/ffd700:过期时间: {new DateTime(cdk.Utiltime).ToLocalTime()}]")); + sb.Append(GetString($"[c/ffd700:{cdk.Name}] [c/00FF00:剩余使用次数: {cdk.Usetime}] [c/ffd700:过期时间: {new DateTime(cdk.Utiltime).ToLocalTime()}] ")); + if(cdk.Playerlimit != "") + { + sb.Append(GetString($"[c/ffd700:玩家限制: {cdk.Playerlimit}] ")); + } + if(cdk.Grouplimit != "") + { + sb.Append(GetString($"[c/ffd700:组限制: {cdk.Grouplimit}] ")); + } + if(cdk.Cmds != "") + { + sb.Append(GetString($"[c/ffd700:指令: {cdk.Cmds}] ")); + } + if(cdk.Used != "") + { + sb.Append(GetString($"[c/ffd700:已使用玩家: {cdk.Used}] ")); + } + sb.AppendLine(); } args.Player.SendInfoMessage(sb.ToString().Trim()); } - [Main] + [Alias("use")] [RealPlayer] public static void Use(CommandArgs args, string cdk) { var cdkData = CDK.GetData(cdk); if (cdkData == null) { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:CDK不存在或已失效]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:CDK不存在或已失效]")); return; } @@ -109,7 +128,7 @@ public static void Use(CommandArgs args, string cdk) { if (!cdkData.Playerlimit.Contains(args.Player.Name)) { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你不在该CDK的领取名单中]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:你不在该CDK的领取名单中]")); return; } } @@ -117,31 +136,31 @@ public static void Use(CommandArgs args, string cdk) { if (!cdkData.Grouplimit.Contains(args.Player.Group.Name)) { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你所在的组不能领取该CDK]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:你所在的组不能领取该CDK]")); return; } } if (cdkData.Usetime < 1) { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你手慢了, 该CDK已经被领完]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:你手慢了, 该CDK已经被领完]")); return; } var time2 = Convert.ToDateTime(DateTime.Now.ToString()).Ticks; var min = (cdkData.Utiltime - time2) / 10000000; if (min <= 0) { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:你来晚了, 该CDK已经过期了]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:你来晚了, 该CDK已经过期了]")); return; } if (cdkData.Used.Contains(args.Player.Name)) { - args.Player.SendInfoMessage(GetString("[c/FF0000:【CDK】]\n[c/ffd700:已经领取过了,不能太贪心哦]")); + args.Player.SendInfoMessage(GetString("[c/ffd700:已经领取过了,不能太贪心哦]")); return; } var strcmd = cdkData.Cmds.Replace("[plr]", args.Player.Name); var cmds = strcmd.Split(','); args.Player.ExecCommand(cmds); CDK.Update(cdkData.Name, cdkData.Usetime - 1, cdkData.Utiltime, cdkData.Grouplimit, cdkData.Playerlimit, (cdkData.Used == "") ? args.Player.Name : cdkData.Used + "," + args.Player.Name, cdkData.Cmds); - args.Player.SendSuccessMessage(GetString("[c/00FF00:【CDK】]\n[c/ffd700:CDK兑换成功!]")); + args.Player.SendSuccessMessage(GetString("[c/ffd700:CDK兑换成功!]")); } } diff --git a/src/TrCDK/Data.cs b/src/TrCDK/Data.cs index 5d25903d6..d26dd90d2 100644 --- a/src/TrCDK/Data.cs +++ b/src/TrCDK/Data.cs @@ -33,7 +33,7 @@ public class CDK : RecordBase public static bool Insert(string CDKname, int Usetime, long Utiltime, string Grouplimit, string Playerlimit, string Cmds) { - _context.Records.Append(new CDK + return _context.Insert(new CDK { Name = CDKname, Usetime = Usetime, @@ -42,8 +42,7 @@ public static bool Insert(string CDKname, int Usetime, long Utiltime, string Gro Playerlimit = Playerlimit, Used = "", Cmds = Cmds - }); - return true; + }) == 1; } public static void Update(string CDKname, int Usetime, long Utiltime, string Grouplimit, string Playerlimit, string Used, string Cmds) diff --git a/src/TrCDK/Main.cs b/src/TrCDK/Main.cs index d27ea7958..ff1c190d0 100644 --- a/src/TrCDK/Main.cs +++ b/src/TrCDK/Main.cs @@ -4,11 +4,12 @@ using System.Globalization; using Microsoft.Xna.Framework; using LazyAPI.Extensions; +using LazyAPI; namespace TrCDK; [ApiVersion(2, 1)] -public class TrCDK : TerrariaPlugin +public class TrCDK : LazyPlugin { public override string Author => "Jonesn"; public override string Description => GetString("CDK系统"); From 91cd490f71d578d22ea92f9d2be7608ee19413fb Mon Sep 17 00:00:00 2001 From: Cai <13110818005@qq.com> Date: Wed, 2 Jul 2025 10:48:36 +0800 Subject: [PATCH 09/20] =?UTF-8?q?docs(TrCDK):=20=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=97=A5=E5=BF=97=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/TrCDK/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TrCDK/README.md b/src/TrCDK/README.md index 7d632b88e..e1ceb586b 100644 --- a/src/TrCDK/README.md +++ b/src/TrCDK/README.md @@ -27,6 +27,7 @@ ### v1.0.0.0 初始版本发布。 +- 初始版本发布 ## 反馈 - 优先发issued -> 共同维护的插件库:https://github.com/UnrealMultiple/TShockPlugin From deb410bad1c71ce9e19d2d2a36e470019ca4f68b Mon Sep 17 00:00:00 2001 From: Cai <13110818005@qq.com> Date: Wed, 2 Jul 2025 10:50:08 +0800 Subject: [PATCH 10/20] =?UTF-8?q?refactor(EssentialsPlus):=20=E7=94=A8`REP?= =?UTF-8?q?LACE`=E6=9B=BF=E4=BB=A3`INSERT=20OR=20REPLACE`=E4=BB=A5?= =?UTF-8?q?=E5=85=BC=E5=AE=B9MySQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/EssentialsPlus/Db/TpAllowManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EssentialsPlus/Db/TpAllowManager.cs b/src/EssentialsPlus/Db/TpAllowManager.cs index 2ca133f82..c4bc7e6e9 100644 --- a/src/EssentialsPlus/Db/TpAllowManager.cs +++ b/src/EssentialsPlus/Db/TpAllowManager.cs @@ -58,7 +58,7 @@ public bool ToggleTpAllow(TSPlayer player) existing.IsEnabled = newState; } - if (this.db.Query("INSERT OR REPLACE INTO TpAllows VALUES (@0, @1)", + if (this.db.Query("REPLACE INTO TpAllows VALUES (@0, @1);", player.Name, newState ? 1 : 0) > 0) { player.TPAllow = newState; From 62b10baf6e837ad48f14716962145c5d04879416 Mon Sep 17 00:00:00 2001 From: Cai <13110818005@qq.com> Date: Fri, 4 Jul 2025 20:41:39 +0800 Subject: [PATCH 11/20] =?UTF-8?q?docs(TrCDK):=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E5=A4=9A=E4=BD=99=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/TrCDK/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TrCDK/README.md b/src/TrCDK/README.md index e1ceb586b..6aa0ae139 100644 --- a/src/TrCDK/README.md +++ b/src/TrCDK/README.md @@ -26,7 +26,6 @@ ## 更新日志 ### v1.0.0.0 -初始版本发布。 - 初始版本发布 ## 反馈 From 8b72411990a5b60c8a26f5bc1a158f488597058f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 5 Jul 2025 02:06:03 +0000 Subject: [PATCH 12/20] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=AD=90=E6=A8=A1=E5=9D=97=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Submodules/VBY.Plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Submodules/VBY.Plugins b/src/Submodules/VBY.Plugins index dc74f9d4f..0de5227ed 160000 --- a/src/Submodules/VBY.Plugins +++ b/src/Submodules/VBY.Plugins @@ -1 +1 @@ -Subproject commit dc74f9d4fd1de14acc74833ca157f22635a9cc88 +Subproject commit 0de5227ed1c2b6ebd2f5c59ab902b600f947600d From 1c19f734cb2beb7153147025f676c71fe379d762 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 21 Jul 2025 02:30:02 +0000 Subject: [PATCH 13/20] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=AD=90=E6=A8=A1=E5=9D=97=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Submodules/VBY.Plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Submodules/VBY.Plugins b/src/Submodules/VBY.Plugins index 0de5227ed..93d852fac 160000 --- a/src/Submodules/VBY.Plugins +++ b/src/Submodules/VBY.Plugins @@ -1 +1 @@ -Subproject commit 0de5227ed1c2b6ebd2f5c59ab902b600f947600d +Subproject commit 93d852fac03edf6285d6bf83e3ef91434b25aafd From 7987f58bf4cfc27ef9034e97045baad1093a0b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=86=99=E6=81=A9?= <2383759126@qq.com> Date: Mon, 4 Aug 2025 22:24:06 +0800 Subject: [PATCH 14/20] =?UTF-8?q?update=EF=BC=9A=E4=BD=9C=E4=B8=BA?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E6=8F=92=E4=BB=B6=EF=BC=8C=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E4=BE=9D=E8=B5=96LazyAPI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ezperm/Configuration.cs | 61 +++++++++++++++++++++++++------------ src/Ezperm/Ezperm.cs | 17 ++++++++--- src/Ezperm/Ezperm.csproj | 6 +--- src/Ezperm/README.md | 3 +- 4 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/Ezperm/Configuration.cs b/src/Ezperm/Configuration.cs index d7a7a5cc1..4090df5ea 100644 --- a/src/Ezperm/Configuration.cs +++ b/src/Ezperm/Configuration.cs @@ -1,40 +1,63 @@ -using LazyAPI.Attributes; -using LazyAPI.ConfigFiles; -using Newtonsoft.Json; +using Newtonsoft.Json; using System.Collections.Generic; using System.IO; using TShockAPI; namespace Ezperm; -[Config] -internal class Configuration : JsonConfigBase +public class Configuration { - protected override string Filename => "ezperm"; - internal class GroupInfo + public const string Path = "tshock/ezperm.json"; + + public static Configuration Instance = new(); + + public void Write() + { + var value = JsonConvert.SerializeObject(this, Formatting.Indented); + File.WriteAllText(Path, value); + } + + public static void Read() + { + if (!File.Exists(Path)) + { + Instance = new Configuration(); + Instance.Write(); + } + else + { + try + { + var content = File.ReadAllText(Path); + var deserializedInstance = JsonConvert.DeserializeObject(content) ?? throw new Exception("配置内容为空或无法解析"); + Instance = deserializedInstance; + } + catch (Exception ex) + { + Console.WriteLine($"加载配置文件失败: {ex.Message}"); + } + } + } + + public class GroupInfo { - [LocalizedPropertyName(CultureType.Chinese, "组名字")] - [LocalizedPropertyName(CultureType.English, "Name")] + [JsonProperty("组名字")] public string Name { get; set; } = ""; - [LocalizedPropertyName(CultureType.Chinese, "父组")] - [LocalizedPropertyName(CultureType.English, "Parent")] + [JsonProperty("父组")] public string Parent { get; set; } = ""; - [LocalizedPropertyName(CultureType.Chinese, "添加的权限")] - [LocalizedPropertyName(CultureType.English, "AddPermissions")] + [JsonProperty("添加的权限")] public List AddPermissions { get; set; } = new List(); - [LocalizedPropertyName(CultureType.Chinese, "删除的权限")] - [LocalizedPropertyName(CultureType.English, "DelPermissions")] + [JsonProperty("删除的权限")] public List DelPermissions { get; set; } = new List(); } - [LocalizedPropertyName(CultureType.Chinese, "组列表", Order = -3)] - [LocalizedPropertyName(CultureType.English, "Groups", Order = -3)] + [JsonProperty("组列表")] public List Groups { get; set; } = new List(); - protected override void SetDefault() + public Configuration() { this.Groups = new List { @@ -65,4 +88,4 @@ protected override void SetDefault() } }; } -} +} \ No newline at end of file diff --git a/src/Ezperm/Ezperm.cs b/src/Ezperm/Ezperm.cs index 8d3afaae5..2f3c5de22 100644 --- a/src/Ezperm/Ezperm.cs +++ b/src/Ezperm/Ezperm.cs @@ -1,5 +1,4 @@ -using LazyAPI; -using Terraria; +using Terraria; using TerrariaApi.Server; using TShockAPI; using TShockAPI.DB; @@ -8,25 +7,35 @@ namespace Ezperm; [ApiVersion(2, 1)] -public class Ezperm : LazyPlugin +public class Ezperm : TerrariaPlugin { public override string Name => System.Reflection.Assembly.GetExecutingAssembly().GetName().Name!; public override string Author => "大豆子,肝帝熙恩优化1449"; public override string Description => GetString("一个指令帮助小白给初始服务器添加缺失的权限,还可以批量添删权限"); - public override Version Version => new Version(1, 3, 0); + public override Version Version => new Version(1, 3, 1); public Ezperm(Main game) : base(game) { } public override void Initialize() { + Configuration.Read(); Commands.ChatCommands.Add(new Command("inperms.admin", this.Cmd, "inperms", "批量改权限")); + GeneralHooks.ReloadEvent += this.GeneralHooksOnReloadEvent; } + + private void GeneralHooksOnReloadEvent(ReloadEventArgs e) + { + Configuration.Read(); + e.Player.SendSuccessMessage(GetString("[Ezperm]插件配置已重载~")); + } + protected override void Dispose(bool disposing) { if (disposing) { Commands.ChatCommands.RemoveAll(x => x.CommandDelegate == this.Cmd); + GeneralHooks.ReloadEvent -= this.GeneralHooksOnReloadEvent; } base.Dispose(disposing); } diff --git a/src/Ezperm/Ezperm.csproj b/src/Ezperm/Ezperm.csproj index 542a88bf0..6a1afdace 100644 --- a/src/Ezperm/Ezperm.csproj +++ b/src/Ezperm/Ezperm.csproj @@ -1,9 +1,5 @@ - + - - - - diff --git a/src/Ezperm/README.md b/src/Ezperm/README.md index 7f9d33170..15243fda7 100644 --- a/src/Ezperm/README.md +++ b/src/Ezperm/README.md @@ -48,7 +48,8 @@ ``` ## 更新日志 - +### v1.3.1 +- 作为基础插件,不再依赖LazyAPI ### v1.3.0 - 添加三个默认权限 ### v1.2.9 From d36efa76a870b69cf6080f172302bff27d8016f3 Mon Sep 17 00:00:00 2001 From: Cai <13110818005@qq.com> Date: Wed, 27 Aug 2025 20:24:22 +0800 Subject: [PATCH 15/20] feat(LazyAPI): add `HelpText` support --- src/LazyAPI/Attributes/CommonAttribute.cs | 5 +++++ src/LazyAPI/Commands/CommandHelper.cs | 13 +++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/LazyAPI/Attributes/CommonAttribute.cs b/src/LazyAPI/Attributes/CommonAttribute.cs index 65d82eb99..aeb721ec9 100644 --- a/src/LazyAPI/Attributes/CommonAttribute.cs +++ b/src/LazyAPI/Attributes/CommonAttribute.cs @@ -54,4 +54,9 @@ public class AliasAttribute(params string[] aliases) : Attribute public HashSet alias = [.. aliases]; } +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] +public class HelpTextAttribute(string helpText) : Attribute +{ + public string helpText = helpText; +} diff --git a/src/LazyAPI/Commands/CommandHelper.cs b/src/LazyAPI/Commands/CommandHelper.cs index cace62b91..e4885faab 100644 --- a/src/LazyAPI/Commands/CommandHelper.cs +++ b/src/LazyAPI/Commands/CommandHelper.cs @@ -96,6 +96,11 @@ private static IEnumerable GetCommandAlias(MemberInfo info) yield return info.Name.ToLower(); } + + private static string GetCommandHelpText(MemberInfo info) + { + return info.GetCustomAttributes().Select(a => a.helpText).FirstOrDefault() ?? GetString("No help available."); + } internal static string[] Register(Type type) @@ -105,10 +110,14 @@ internal static string[] Register(Type type) Console.WriteLine($"Command `{type.FullName}` should be static"); } var names = GetCommandAlias(type).ToArray(); + var helpText = GetCommandHelpText(type); var tree = BuildTree(type, AliasToString(names)); - + TShockAPI.Commands.ChatCommands.Add(new TShockAPI.Command(args => ParseCommand(tree, args), - names)); + names) + { + HelpText = helpText + }); return names; } From b102e4ac8b9e4b25a447bf261522ecb556c08c49 Mon Sep 17 00:00:00 2001 From: Cai <13110818005@qq.com> Date: Wed, 27 Aug 2025 20:28:51 +0800 Subject: [PATCH 16/20] fix(LazyAPI): remove method target for `HelpTextAttribute` --- src/LazyAPI/Attributes/CommonAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LazyAPI/Attributes/CommonAttribute.cs b/src/LazyAPI/Attributes/CommonAttribute.cs index aeb721ec9..03bd11e48 100644 --- a/src/LazyAPI/Attributes/CommonAttribute.cs +++ b/src/LazyAPI/Attributes/CommonAttribute.cs @@ -54,7 +54,7 @@ public class AliasAttribute(params string[] aliases) : Attribute public HashSet alias = [.. aliases]; } -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] +[AttributeUsage(AttributeTargets.Class)] public class HelpTextAttribute(string helpText) : Attribute { public string helpText = helpText; From 89ee85a0dd22c264aa18309c7d6ccc6fb2108310 Mon Sep 17 00:00:00 2001 From: Cai <13110818005@qq.com> Date: Wed, 27 Aug 2025 22:41:41 +0800 Subject: [PATCH 17/20] feat(LazyAPI): add `UsageAttribute` to set custom usage --- src/LazyAPI/Attributes/CommonAttribute.cs | 5 +++++ src/LazyAPI/Commands/Command.HelpCommand.cs | 6 ++++-- src/LazyAPI/Commands/Command.cs | 2 +- src/LazyAPI/Commands/CommandBase.cs | 6 +++--- src/LazyAPI/Commands/CommandHelper.cs | 11 +++++++++++ src/LazyAPI/Commands/CommandParser.cs | 14 +++++++------- src/LazyAPI/Commands/FlexibleCommand.cs | 2 +- src/LazyAPI/Commands/SingleCommand.cs | 2 +- src/TrCDK/Command.cs | 13 ++++++++----- src/TrCDK/README.md | 16 ++++++++-------- 10 files changed, 49 insertions(+), 28 deletions(-) diff --git a/src/LazyAPI/Attributes/CommonAttribute.cs b/src/LazyAPI/Attributes/CommonAttribute.cs index 03bd11e48..74a6626a1 100644 --- a/src/LazyAPI/Attributes/CommonAttribute.cs +++ b/src/LazyAPI/Attributes/CommonAttribute.cs @@ -60,3 +60,8 @@ public class HelpTextAttribute(string helpText) : Attribute public string helpText = helpText; } +[AttributeUsage(AttributeTargets.Method)] +public class UsageAttribute(string usage) : Attribute +{ + public string usage = usage; +} \ No newline at end of file diff --git a/src/LazyAPI/Commands/Command.HelpCommand.cs b/src/LazyAPI/Commands/Command.HelpCommand.cs index 79d261d37..2933d9c0e 100644 --- a/src/LazyAPI/Commands/Command.HelpCommand.cs +++ b/src/LazyAPI/Commands/Command.HelpCommand.cs @@ -11,7 +11,7 @@ public HelpCommand(Command parent, string infoPrefix) { this.parent = parent; this.permissions = []; - this.info = infoPrefix + "help"; + this.usage = GetString($"{infoPrefix}help - list available usages"); } public override ParseResult TryParse(CommandArgs args, int current) @@ -27,7 +27,9 @@ public override ParseResult TryParse(CommandArgs args, int current) } args.Player.SendInfoMessage(GetString("available usage:")); - foreach (var sub in this.parent._dict.Values.SelectMany(subs => subs).Concat(this.parent._main).Distinct() + foreach (var sub in this.parent._dict.Values + .SelectMany(subs => subs) + .Concat(this.parent._main).Distinct() .Where(sub => sub.CanExec(args.Player))) { args.Player.SendInfoMessage(sub.ToString()); diff --git a/src/LazyAPI/Commands/Command.cs b/src/LazyAPI/Commands/Command.cs index 93067a022..2c41f0911 100644 --- a/src/LazyAPI/Commands/Command.cs +++ b/src/LazyAPI/Commands/Command.cs @@ -11,7 +11,7 @@ internal partial class Command : CommandBase public Command(MemberInfo type, string infoPrefix) : base(type) { - this.info = $"{infoPrefix} <...>"; + this.usage = CommandHelper.GetCommandUsage(type) ?? $"{infoPrefix} <...>"; this._infoPrefix = infoPrefix; } diff --git a/src/LazyAPI/Commands/CommandBase.cs b/src/LazyAPI/Commands/CommandBase.cs index 66d73e7a6..51bc639a2 100644 --- a/src/LazyAPI/Commands/CommandBase.cs +++ b/src/LazyAPI/Commands/CommandBase.cs @@ -17,12 +17,12 @@ protected internal readonly struct ParseResult(CommandBase current, int num) protected string[] permissions; private readonly bool _realPlayer; - protected string? info; + internal string? usage; public abstract ParseResult TryParse(CommandArgs args, int current); public override string? ToString() { - return this.info; + return this.usage; } protected CommandBase(MemberInfo member) @@ -68,6 +68,6 @@ protected bool CheckPlayer(TSPlayer plr) } protected ParseResult GetResult(int num) { - return new(this, num); + return new ParseResult(this, num); } } diff --git a/src/LazyAPI/Commands/CommandHelper.cs b/src/LazyAPI/Commands/CommandHelper.cs index e4885faab..12fc5c706 100644 --- a/src/LazyAPI/Commands/CommandHelper.cs +++ b/src/LazyAPI/Commands/CommandHelper.cs @@ -54,6 +54,7 @@ private static Command BuildTree(Type type, string prefix) { var al = GetAlias(func).ToArray(); CommandBase sub = func.GetCustomAttribute() != null ? new FlexibleCommand(func, prefix + AliasToString(al)) : new SingleCommand(func, prefix + AliasToString(al)); + foreach (var alias in al) { result.Add(alias, sub); @@ -68,6 +69,11 @@ private static Command BuildTree(Type type, string prefix) private static void ParseCommand(Command tree, CommandArgs args) { + if (args.Parameters.Count == 0) + { + args.Parameters.Add("help"); + } + var result = tree.TryParse(args, 0); if (result.unmatched == 0) { @@ -97,6 +103,11 @@ private static IEnumerable GetCommandAlias(MemberInfo info) yield return info.Name.ToLower(); } + internal static string? GetCommandUsage(MemberInfo info) + { + return info.GetCustomAttributes().Select(a => a.usage).FirstOrDefault(); + } + private static string GetCommandHelpText(MemberInfo info) { return info.GetCustomAttributes().Select(a => a.helpText).FirstOrDefault() ?? GetString("No help available."); diff --git a/src/LazyAPI/Commands/CommandParser.cs b/src/LazyAPI/Commands/CommandParser.cs index a0b1805d4..353a92ef6 100644 --- a/src/LazyAPI/Commands/CommandParser.cs +++ b/src/LazyAPI/Commands/CommandParser.cs @@ -77,13 +77,13 @@ private static bool TryParseAccount(string arg, out object obj) private static readonly Dictionary friendlyName = new() { - [typeof(bool)] = "bool", - [typeof(int)] = "int", - [typeof(long)] = "long", - [typeof(string)] = "str", - [typeof(DateTime)] = "date", - [typeof(TSPlayer)] = "player", - [typeof(UserAccount)] = "account" + [typeof(bool)] = GetString("bool"), + [typeof(int)] = GetString("int"), + [typeof(long)] = GetString("long"), + [typeof(string)] = GetString("str"), + [typeof(DateTime)] = GetString("date"), + [typeof(TSPlayer)] = GetString("player"), + [typeof(UserAccount)] = GetString("account") }; public static Parser GetParser(Type type) diff --git a/src/LazyAPI/Commands/FlexibleCommand.cs b/src/LazyAPI/Commands/FlexibleCommand.cs index 51574511e..fdeb7cce0 100644 --- a/src/LazyAPI/Commands/FlexibleCommand.cs +++ b/src/LazyAPI/Commands/FlexibleCommand.cs @@ -33,7 +33,7 @@ public FlexibleCommand(MethodInfo method, string infoPrefix) : base(method) } this.argParsers = [.. ap]; this.defaultParsers = [.. dp]; - this.info = sb.ToString(); + this.usage = CommandHelper.GetCommandUsage(method) ?? sb.ToString(); this.method = method.GetFastInvoker(); } diff --git a/src/LazyAPI/Commands/SingleCommand.cs b/src/LazyAPI/Commands/SingleCommand.cs index 225510e78..4f072b540 100644 --- a/src/LazyAPI/Commands/SingleCommand.cs +++ b/src/LazyAPI/Commands/SingleCommand.cs @@ -24,7 +24,7 @@ public SingleCommand(MethodInfo method, string infoPrefix) : base(method) } this.argParsers = [.. ap]; - this.info = sb.ToString(); + this.usage = CommandHelper.GetCommandUsage(method) ?? sb.ToString(); this.method = method.GetFastInvoker(); } diff --git a/src/TrCDK/Command.cs b/src/TrCDK/Command.cs index 24afe175a..97ccc26b3 100644 --- a/src/TrCDK/Command.cs +++ b/src/TrCDK/Command.cs @@ -1,20 +1,18 @@ using LazyAPI.Attributes; using LazyAPI.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; using TShockAPI; namespace TrCDK; [Command("cdk")] [Permissions("cdk.use")] +[HelpText("CDK系统主命令")] public class Command { [Alias("give")] [Permissions("cdk.give")] + [Usage("cdk give <玩家名> <指令列表> - 给玩家CDK奖励")] public static void Give(CommandArgs args, TSPlayer? player, string cmd) { if(player == null) @@ -37,7 +35,8 @@ public static void Give(CommandArgs args, TSPlayer? player, string cmd) [Alias("update")] [Flexible] [Permissions("cdk.update")] - public static void Update(CommandArgs args, string cdkname, int usetime, long utiltime, string grouplimit = "", string playerlimit = "", string cmds = "") + [Usage("cdk update <使用次数> <过期时间> <指令> [组限制] [玩家限制] --- 更新CDK信息")] + public static void Update(CommandArgs args, string cdkname, int usetime, long utiltime, string cmds = "" ,string grouplimit = "", string playerlimit = "") { var cdkData = CDK.GetData(cdkname); if (cdkData == null) @@ -51,6 +50,7 @@ public static void Update(CommandArgs args, string cdkname, int usetime, long ut [Alias("del")] [Permissions("cdk.del")] + [Usage("cdk del - 删除指定CDK")] public static void Delete(CommandArgs args, string cdkname) { if (CDK.GetData(cdkname) == null) @@ -65,6 +65,7 @@ public static void Delete(CommandArgs args, string cdkname) [Alias("add")] [Flexible] [Permissions("cdk.add")] + [Usage("cdk add <使用次数> <过期时间> <指令> [组限制] [玩家限制] - 添加新CDK")] public static void Add(CommandArgs args, string cdkname, int usetime, long utiltime, string cmds, string grouplimit = "", string playerlimit = "") { if (CDK.Insert(cdkname, usetime, utiltime, grouplimit, playerlimit, cmds)) @@ -79,6 +80,7 @@ public static void Add(CommandArgs args, string cdkname, int usetime, long utilt [Alias("list")] [Permissions("cdk.list")] + [Usage("cdk list - 列出所有CDK")] public static void List(CommandArgs args) { var cdkList = CDK.GetAllData(); @@ -115,6 +117,7 @@ public static void List(CommandArgs args) [Alias("use")] [RealPlayer] + [Usage("cdk use - 兑换CDK礼包")] public static void Use(CommandArgs args, string cdk) { var cdkData = CDK.GetData(cdk); diff --git a/src/TrCDK/README.md b/src/TrCDK/README.md index 6aa0ae139..d717309a3 100644 --- a/src/TrCDK/README.md +++ b/src/TrCDK/README.md @@ -6,14 +6,14 @@ - CDK可以设置使用次数、过期时间、组限制、玩家限制。 ## 指令 -| 语法 | 权限 | 说明 | -| --- | --- | --- | -| /cdk `` | cdk.use | 玩家兑换 CDK 礼包 | -| /cdkloadall | cdk.admin.loadall | 显示所有 CDK 列表 | -| /cdkadd `` `<使用次数>` `<过期时间>` `<组限制>` `<玩家限制>` `<指令>` | cdk.admin.add | 添加新 CDK | -| /cdkdel `` | cdk.admin.del | 删除指定 CDK | -| /cdkupdate `` `<使用次数>` `<过期时间>` `<组限制>` `<玩家限制>` `<已使用玩家>` `<指令>` | cdk.admin.update | 更新 CDK 信息 | -| /cdkgive `<玩家名>` `<指令列表>` | cdk.admin.give | 给玩家 CDK 奖励 | +| 语法 | 权限 | 说明 | +|-------------------------------------------------------------|-------------------|-------------| +| /cdk use | cdk.use | 玩家兑换 CDK 礼包 | +| /cdk list | cdk.admin.loadall | 显示所有 CDK 列表 | +| /cdk add `` `<使用次数>` `<过期时间>` `<指令>` `[组限制]` `[玩家限制]` | cdk.admin.add | 添加新 CDK | +| /cdk del `` | cdk.admin.del | 删除指定 CDK | +| /cdk update `` `<使用次数>` `<过期时间>` `<指令>` `[组限制]` `[玩家限制]` | cdk.admin.update | 更新 CDK 信息 | +| /cdk give `<玩家名>` `<指令列表>` | cdk.admin.give | 给玩家 CDK 奖励 | ### 注意事项 - **/cdkadd 和 /cdkupdate**:过期时间格式 `yyyy-MM-ddThh:mm`,如 `2024-12-31T23:59`。组和玩家限制用逗号分隔,不限制填 `none`。 From c065ae03df26dc7ade67b1d48eac51c8301275d5 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 21 Sep 2025 02:05:03 +0000 Subject: [PATCH 18/20] =?UTF-8?q?chore:=20=E8=87=AA=E5=8A=A8=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=AD=90=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Submodules/VBY.Plugins | 2 +- src/Submodules/Yaaiomni | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Submodules/VBY.Plugins b/src/Submodules/VBY.Plugins index 93d852fac..11b10c41f 160000 --- a/src/Submodules/VBY.Plugins +++ b/src/Submodules/VBY.Plugins @@ -1 +1 @@ -Subproject commit 93d852fac03edf6285d6bf83e3ef91434b25aafd +Subproject commit 11b10c41f0f75a1f25fb4918e3875877281aac10 diff --git a/src/Submodules/Yaaiomni b/src/Submodules/Yaaiomni index 01048770b..5e5db6fc9 160000 --- a/src/Submodules/Yaaiomni +++ b/src/Submodules/Yaaiomni @@ -1 +1 @@ -Subproject commit 01048770b91b830a3d1ef3a7ff7eb313aafb5124 +Subproject commit 5e5db6fc9ef96783e33bd9d4be6294b8a311ab75 From d2530c0b60abfb3b243e123bca3678881e3dc4e1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 22 Sep 2025 02:04:55 +0000 Subject: [PATCH 19/20] =?UTF-8?q?chore:=20=E8=87=AA=E5=8A=A8=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=AD=90=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Submodules/VBY.Plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Submodules/VBY.Plugins b/src/Submodules/VBY.Plugins index 11b10c41f..e5cfb3bbd 160000 --- a/src/Submodules/VBY.Plugins +++ b/src/Submodules/VBY.Plugins @@ -1 +1 @@ -Subproject commit 11b10c41f0f75a1f25fb4918e3875877281aac10 +Subproject commit e5cfb3bbd3e056728d29cd648ab8ef2c1eb97b2c From 3bf350e53437e7c64efa37027c80ac668d258f20 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 23 Sep 2025 01:54:38 +0000 Subject: [PATCH 20/20] =?UTF-8?q?chore:=20=E8=87=AA=E5=8A=A8=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=AD=90=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Submodules/VBY.Plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Submodules/VBY.Plugins b/src/Submodules/VBY.Plugins index e5cfb3bbd..a03d1c180 160000 --- a/src/Submodules/VBY.Plugins +++ b/src/Submodules/VBY.Plugins @@ -1 +1 @@ -Subproject commit e5cfb3bbd3e056728d29cd648ab8ef2c1eb97b2c +Subproject commit a03d1c180c0292088d7f7af6ba9e20df72f0a308