diff --git a/Plugin.sln b/Plugin.sln index 958fcc39..e227c4dd 100644 --- a/Plugin.sln +++ b/Plugin.sln @@ -252,6 +252,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CaiPacketDebug", "src\CaiPa EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ItemDecoration", "src\ItemDecoration\ItemDecoration.csproj", "{C4A6D861-D53D-447E-9948-A23A797E2C1C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DwTP", "src\DwTP\DwTP.csproj", "{2003D2E7-0A1C-4557-A0EE-F4E06B766B78}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1236,6 +1238,14 @@ Global {C4A6D861-D53D-447E-9948-A23A797E2C1C}.Release|Any CPU.Build.0 = Release|Any CPU {C4A6D861-D53D-447E-9948-A23A797E2C1C}.Release|x64.ActiveCfg = Release|Any CPU {C4A6D861-D53D-447E-9948-A23A797E2C1C}.Release|x64.Build.0 = Release|Any CPU + {2003D2E7-0A1C-4557-A0EE-F4E06B766B78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2003D2E7-0A1C-4557-A0EE-F4E06B766B78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2003D2E7-0A1C-4557-A0EE-F4E06B766B78}.Debug|x64.ActiveCfg = Debug|Any CPU + {2003D2E7-0A1C-4557-A0EE-F4E06B766B78}.Debug|x64.Build.0 = Debug|Any CPU + {2003D2E7-0A1C-4557-A0EE-F4E06B766B78}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2003D2E7-0A1C-4557-A0EE-F4E06B766B78}.Release|Any CPU.Build.0 = Release|Any CPU + {2003D2E7-0A1C-4557-A0EE-F4E06B766B78}.Release|x64.ActiveCfg = Release|Any CPU + {2003D2E7-0A1C-4557-A0EE-F4E06B766B78}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README_EN.md b/README_EN.md index 7eedd92e..a19d911b 100644 --- a/README_EN.md +++ b/README_EN.md @@ -104,7 +104,8 @@ | [DeathDrop](src/DeathDrop/README.md) | No | Random and custom loot upon monster death | None | | [DisableMonsLoot](src/DisableMonsLoot/README.md) | No | Prohibit monster loot | None | | [DisableSurfaceProjectiles](src/DisableSurfaceProjectiles/README.md) | No | Prohibit surface projectiles | None | -| [Don't Fuck](src/DonotFuck/README.md) | No | Prevent swearing | None | +| [Don't Fuck](src/DonotFuck/README.md) | Yes | Prevent swearing | None | +| [DwTP](src/DwTP/README.md) | Yes | Positioning Teleport | None | | [DTEntryBlock](src/DTEntryBlock/README.md) | No | Prevent entry into dungeons or temples | None | | [DumpTerrariaID](src/DumpTerrariaID/README.md) | No | Dump Terraria IDs | None | | [Economics.Deal](src/Economics.RPG/README_EN.md) | Yes | Trading plugin | [EconomicsAPI](src/EconomicsAPI/README_EN.md) | diff --git a/src/AutoAirItem/AutoAirItem.cs b/src/AutoAirItem/AutoAirItem.cs index 8ed119e6..575c9c38 100644 --- a/src/AutoAirItem/AutoAirItem.cs +++ b/src/AutoAirItem/AutoAirItem.cs @@ -12,7 +12,7 @@ public class AutoAirItem : TerrariaPlugin #region 插件信息 public override string Name => "自动垃圾桶"; public override string Author => "羽学"; - public override Version Version => new Version(1, 1, 6); + public override Version Version => new Version(1, 1, 7); public override string Description => "涡轮增压不蒸鸭"; #endregion @@ -170,7 +170,7 @@ public static void UpDict(Dictionary delItem, int type, int stack) else { // 直接添加新ID和它的数量 - delItem.Add(type, stack); + delItem.Add(type, 0); } } #endregion diff --git a/src/AutoAirItem/README.md b/src/AutoAirItem/README.md index ba1e15a7..8c949724 100644 --- a/src/AutoAirItem/README.md +++ b/src/AutoAirItem/README.md @@ -9,6 +9,9 @@ ## 更新日志 ``` +v1.1.7 +修复PE使用:/air del 会返还双倍物品的BUG + v1.1.6 加入了/air ck 数量指令 筛选出物品超过此数量的玩家 @@ -85,17 +88,6 @@ v1.0.0 | /airreset | /重置垃圾桶| AutoAir.admin | 清空玩家数据表(重置服务器用) | | /reload | 无 | tshock.cfg.reload | 重载配置文件 | ---- -注意事项 ---- -1.`如何给玩家添加权限`请使用该指令在控制台发送: `/group addperm default AutoAir.use` - -2.`垃圾桶开关`与`垃圾桶物品`和均由玩家游戏内指令触发! - -3.`玩家数据表`由玩家加入服务器后`自动写入` - -4.`监听垃圾桶`使用指令`/air auto`开启,当垃圾桶位格放入物品时自动添加垃圾桶表内 - ## 配置 > 配置文件位置:tshock/自动垃圾桶.json ```json diff --git a/src/AutoAirItem/README_EN.md b/src/AutoAirItem/README_EN.md index db7291b9..09cc16ae 100644 --- a/src/AutoAirItem/README_EN.md +++ b/src/AutoAirItem/README_EN.md @@ -7,6 +7,66 @@ - It automatically creates the configuration structure when a player joins the server. - A fully automated plugin that interacts with players through commands. +## Update Log +``` +v1.1.7 +- Fixed the bug where using `/air del` would return double the items on Mobile Games + +v1.1.6 +- Added the `/air ck ` command to filter out players with more than the specified amount of items +- Fixed the bug where returned items exceeded the stack limit + +v1.1.5 +- Changed the removal logic to trigger on player movement (performance optimization) +- Removed the `/air sd` command to modify cleanup speed +- Added the item return logic to the `/air del` command + +v1.1.4 +- Removed the automatic player data cleanup logic and related configuration options +- Changed the `/air reset` command to: + - A separate `/airreset` command for easier loop reset of the server + +v1.1.3 +- Improved the command menu +- Added the `/air sd` command to modify garbage cleanup speed +- Added the `/air reset` command to clear all player data (used to reset the server) +- The `/air mess` command can control the hiding of item addition notifications to the trash bin + +v1.1.2 +- Fixed the null reference when a player leaves the server +- Moved the data cleanup function to the join server event + +v1.1.1 +- Fixed English translation (open-source version) + +v1.1.0 +- Moved the data table content from Config to the internal MyData class to avoid frequent writes to the configuration file, which could cause issues with a large number of players +- Automatically enabled the trash bin feature on first login, with a command prompt when items are placed in the trash bin +- Added online status checks to prevent innocent data cleanup due to 24-hour play sessions +- Added offline announcements when cleaning specific player data + +v1.0.2 +- Added the `/air yes` command to add held items to the trash bin +- Items will only be cleaned if not selected; even if the wrong input is entered, `/air del ` can be used to remove them +- Added the `/air mess` command to enable/disable cleanup messages +- Added a listener for the trash bin slot to automatically add items to the `Trash Bin Table` when they are placed in the trash bin + +v1.0.1 +- Removed the plugin activation status notification on every `/air add` or `/air del` +- Added the "Data Cleanup Cycle" logic: + - The "Record Time" is updated every time a player logs in or out + - If the offline time of Player A and the login time of Player B differ by more than the set "Cleanup Cycle" time, Player A's data will be automatically cleaned + - Setting the "Data Cleanup Cycle" to over 9999999999 hours effectively means never cleaning data + +v1.0.0 +- Implemented an automatic trash bin for Tshock based on the idea from the Mod "Better Experience" +- The garbage cleanup speed unit is frames per second; the smaller the value, the faster the cleanup +- Use the `/air on` command to enable the plugin +- Use `/air add ` or `` or `Alt + Left Click` to select an item, which will automatically write the item name to the configuration file +- Automatically creates a "Player Data Table" based on player login events +- The "Trash Bin Items" will trigger the cleanup logic if they exist and the "Trash Bin Switch" is enabled +- Equipped with "Login Time" for server owners to reference whether they need to manually remove a player's data +``` ## Commands @@ -21,39 +81,13 @@ | /air mess | /垃圾 mess | AutoAir.use | Enable or disable cleanup messages | | /air add or del id | /垃圾 add or del 物品名 | AutoAir.use | Add or remove items from personal trash can list | - ---- -Notes ---- - -1. To give players permission, use this command in the console: `/group addperm default AutoAir.use`. - -2. The "Trash Bin Toggle" and "Trash Bin Items" are both triggered by in-game player commands! - -3. The "Player Data Table" is automatically updated when a player joins the server. If a player with the same name exists, their "record time" will be updated (it will also update once when they are offline). - -4. The unit for "Trash Cleanup Speed" is the frame rate. The smaller the value, the "faster" the cleanup speed. - -5. "Data Cleanup Cycle": If Player A logs out at 00:00 and Player B logs in at 00:10, with a "cleanup cycle of 10 hours," if Player A does not log back in by 10:10, Player A's auto-trash data will be cleared "when Player B logs off." - -6. If the "Data Cleanup Cycle" is set to more than "9999999999 hours," it is equivalent to never clearing the data. - -7. To "Monitor Trash Bin," use the command `/air auto` to enable auto-monitoring. Items placed in the trash bin slot will automatically be added to the trash bin table. - ## Config > Configuration file location:tshock/自动垃圾桶.json ```json { - "插件指令权限": "指令菜单:/air 或 /垃圾,权限名【AutoAir.use】,给玩家权限:/group addperm default AutoAir.use", // Command permissions for the plugin, allowing players to use /air or /trash commands with the AutoAir.use permission - "使用说明": "玩家每次进出服都会更新【记录时间】,玩家A离线时间与玩家B登录时间相差超过【清理周期】所设定的时间,则自动清理该玩家A的数据", // Description of how the plugin works: player record time is updated on join/leave, and data is cleared if the time between Player A logging out and Player B logging in exceeds the set cleanup cycle - "插件开关": true, // Toggle to enable or disable the plugin - "清理垃圾速度": 60, // Speed of trash cleanup, lower values mean faster cleanup - "广告开关": true, // Toggle to enable or disable advertisements - "广告内容": "[i:3456][C/F2F2C7:插件开发] [C/BFDFEA:by] [c/00FFFF:羽学][i:3459]", // Content of the advertisement message - "是否清理数据": true, // Whether or not to clean up player data - "清理数据周期/小时": 24 // The data cleanup cycle in hours + "Plugin Command Permissions": "Command menu: /air or /trash, permission name [AutoAir.use], give players permission: /group addperm default AutoAir.use", + "Plugin Enable": true } - ``` ## FeedBack - Github Issue -> TShockPlugin Repo: https://github.com/UnrealMultiple/TShockPlugin diff --git a/src/AutoFish/AutoFish.cs b/src/AutoFish/AutoFish.cs index 09db79c4..2a88f6b0 100644 --- a/src/AutoFish/AutoFish.cs +++ b/src/AutoFish/AutoFish.cs @@ -15,7 +15,7 @@ public class AutoFish : TerrariaPlugin #region 插件信息 public override string Name => "自动钓鱼"; public override string Author => "羽学 少司命"; - public override Version Version => new Version(1, 3, 1); + public override Version Version => new Version(1, 3, 2); public override string Description => "涡轮增压不蒸鸭"; #endregion @@ -162,18 +162,20 @@ private static void ControlFishing(ProjectileAiUpdateEventArgs args, TSPlayer pl if (inv.bait > 0 && baitTypeUsed == inv.type) { //当物品数量正常则开始进入钓鱼检查 - if (inv.stack > 0) + if (inv.stack > 1) { - //当前物品数量为1则移除(避免选中的饵不会主动消失 变成无限饵 或 卡住线程) - if (plr.TPlayer.inventory[i].stack == 1) - { - inv.TurnToAir(); - } - //发包到对应饵料的格子内 plr.SendData(PacketTypes.PlayerSlot, "", plr.Index, i); break; } + + //当前物品数量为1则移除(避免选中的饵不会主动消失 变成无限饵 或 卡住线程) + if (inv.stack == 1 || inv.bait == 1) + { + inv.TurnToAir(); + plr.SendData(PacketTypes.PlayerSlot, "", plr.Index, i); + break; + } } } } diff --git a/src/AutoFish/README.md b/src/AutoFish/README.md index 0b9777a2..a9c9c5d0 100644 --- a/src/AutoFish/README.md +++ b/src/AutoFish/README.md @@ -12,6 +12,9 @@ ## 更新日志 ``` +v1.3.2 +尝试修复鱼饵数量为1时崩服BUG + v1.3.1 修复了在多钩钓鱼模式下,单体召唤物衍生更多数量BUG 并加了一个配置项来禁止它们"禁止衍生弹幕"。 diff --git a/src/AutoFish/README_EN.md b/src/AutoFish/README_EN.md index f2a1d3bb..551889da 100644 --- a/src/AutoFish/README_EN.md +++ b/src/AutoFish/README_EN.md @@ -14,6 +14,14 @@ ## Update Log ``` +v1.3.2 +- Attempted to fix the server crash bug that occurred when the bait quantity was 1. + +v1.3.1 +- Fixed the bug where in multi-hook fishing mode, individual summons would spawn additional quantities. +- Added a configuration option to disable "spawned bullet hell" from these summons. +- Introduced a random item configuration option; when enabled, players can randomly catch any item while fishing. + v1.3.0 Fixed: Preferred bait would not disappear, turning it into infinite bait or causing thread locking bugs. Modified: Extra catches can now be hooked along with existing environmental items. diff --git a/src/DonotFuck/Ban.cs b/src/DonotFuck/Ban.cs deleted file mode 100644 index 3f1a7b10..00000000 --- a/src/DonotFuck/Ban.cs +++ /dev/null @@ -1,102 +0,0 @@ -using TShockAPI; // 引入TShockAPI命名空间,用于与TShock插件交互。 -using TShockAPI.DB; // 引入TShock数据库操作相关的命名空间。 - -// 命名空间 DonotFuck,用于存放与禁止不当行为相关的功能类。 -namespace DonotFuck -{ - // 定义 Ban 类,用于管理玩家的不当行为检测、封禁及查询。 - public class Ban - { - // 静态变量,封禁操作的执行者名称。 - static string BanningUser = "DonotFuck"; - - // 静态字段,使用字典存储玩家触发不当行为的计数。 - static Dictionary _bans = new Dictionary(); - - // 触发规则方法,记录玩家的不当行为次数。 - public static int Trigger(string name) - { - lock (_bans) // 使用锁机制保护字典的并发访问,防止多线程环境下的数据竞争问题。 - { - if (_bans.ContainsKey(name)) // 检查玩家名称是否已经在记录中。 - { - _bans[name]++; // 如果存在,则增加该玩家的触发次数。 - } - else - { - _bans.Add(name, 1); // 如果不存在,则添加该玩家到字典中,并初始化触发次数为1。 - } - return _bans[name]; // 返回该玩家的当前触发次数。 - } - } - - // 重置玩家的触发次数方法。 - public static void Remove(string name) - { - lock (_bans) // 使用锁保证线程安全。 - { - if (_bans.ContainsKey(name)) // 检查字典中是否存在该玩家的记录。 - { - _bans[name] = 0; // 将玩家的触发次数重置为0。 - } - } - } - - // 添加封禁方法,根据玩家名、原因和持续时间进行封禁。 - public static AddBanResult AddBan(string playerName, string reason, int durationSeconds) - { - DateTime expiration = DateTime.UtcNow.AddSeconds(durationSeconds); // 计算封禁到期时间。 - AddBanResult banResult = TShock.Bans.InsertBan("acc:" + playerName, reason, BanningUser, DateTime.UtcNow, expiration); // 执行封禁操作。 - if (banResult.Ban != null) - { - TShock.Log.Info($"已封禁{playerName}。输入 /ban del {banResult.Ban.TicketNumber} 解除封禁。"); // 日志记录成功封禁。 - } - else - { - TShock.Log.Info($"封禁{playerName}失败!原因: {banResult.Message}。"); // 日志记录封禁失败原因。 - } - return banResult; // 返回封禁操作的结果。 - } - - // 列出封禁方法,展示由本插件执行的所有封禁记录。 - public static void ListBans(CommandArgs args) - { - var bans = ( - from ban in TShock.Bans.Bans // 查询所有封禁记录。 - where ban.Value.BanningUser == BanningUser // 筛选出由本插件执行的封禁。 - orderby ban.Value.ExpirationDateTime descending // 按封禁到期时间降序排列。 - select ban - ).ToList(); - - var lines = new List(); // 用于存储格式化后的封禁记录信息。 - bool flag = false; // 标记是否遇到已过期的封禁记录。 - foreach (var ban in bans) - { - if (!flag && (ban.Value.ExpirationDateTime <= DateTime.UtcNow)) - { - lines.Add("----下面的记录都已失效----"); // 分隔符,表明以下封禁已过期。 - flag = true; - } - // 添加单条封禁记录信息。 - lines.Add($"{ban.Value.Identifier.Substring(4)}, 截止:{ban.Value.ExpirationDateTime.ToLocalTime():yyyy-dd-HH hh:mm:ss}, 原因:{ban.Value.Reason}, 解封:/ban del {ban.Key}"); - } - - if (!lines.Any()) // 如果没有任何封禁记录。 - { - args.Player.SendInfoMessage("没有记录!看来没人作弊(*^▽^*)"); // 向命令发起者发送消息。 - return; - } - - if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out int pageNumber)) // 处理分页参数。 - { - return; - } - PaginationTools.SendPage(args.Player, pageNumber, lines, new PaginationTools.Settings // 分页显示封禁记录。 - { - MaxLinesPerPage = 15, // 每页最多显示15条记录。 - HeaderFormat = "封禁记录 ({0}/{1}):", // 分页标题格式。 - FooterFormat = "输入/cbag ban {{0}}查看更多".SFormat(Commands.Specifier), // 分页底部提示信息。 - }); - } - } -} \ No newline at end of file diff --git a/src/DonotFuck/Commands.cs b/src/DonotFuck/Commands.cs new file mode 100644 index 00000000..70fc713c --- /dev/null +++ b/src/DonotFuck/Commands.cs @@ -0,0 +1,156 @@ +using System.Text; +using TShockAPI; +using static DonotFuck.Plugin; + +namespace DonotFuck; + +internal class Commands +{ + #region 主指令方法 + public static void DFCmd(CommandArgs args) + { + if (args.Parameters.Count == 0) + { + Help(args); + } + + if (args.Parameters.Count == 1) + { + if (args.Parameters[0].ToLower() == "list") + { + ListDirtyWords(args.Player, 1); + return; + } + + if (args.Parameters[0].ToLower() == "log" && args.Player.HasPermission("DonotFuck.admin")) + { + var Enabled = Configuration.Instance.EnableLog; + Configuration.Instance.EnableLog = !Enabled; + var Status = Enabled ? + GetString("禁用"): + GetString("启用"); + + args.Player.SendSuccessMessage($"已{Status}敏感词记录功能。"); + Configuration.Save(); + return; + } + + if (args.Parameters[0].ToLower() == "clear" && args.Player.HasPermission("DonotFuck.admin")) + { + Configuration.Instance.DisposeLog(); + var dirinfo = new DirectoryInfo(Path.Combine(TShock.SavePath, Configuration._Directory)); + foreach (var file in dirinfo.GetFiles("*.log")) + { + file.Delete(); + } + args.Player.SendSuccessMessage(GetString("《脏话纪录》文件夹已成功清空。")); + return; + } + } + + if (args.Parameters.Count == 2) + { + var action = args.Parameters[0].ToLower(); + var word = args.Parameters[1]; + + switch (action) + { + case "add": + if (args.Player.HasPermission("DonotFuck.admin")) + { + if (Configuration.Instance.Add(word)) + { + args.Player.SendSuccessMessage(GetString("已成功将敏感词添加到表中: [c/92C5EC:{0}]!", word)); + } + else + { + args.Player.SendErrorMessage(GetString("[c/92C5EC:{0}] 已在敏感词表中!", word)); + } + } + break; + + case "del": + if (args.Player.HasPermission("DonotFuck.admin")) + { + if (Configuration.Instance.Del(word)) + { + args.Player.SendSuccessMessage(GetString("已成功将敏感词从表中移除: [c/92C5EC:{0}]!", word)); + } + else + { + args.Player.SendErrorMessage(GetString("[c/92C5EC:{0}] 不在敏感词表中!", word)); + } + } + break; + + case "l": + case "list": + + if (int.TryParse(args.Parameters[1], out var page)) + { + ListDirtyWords(args.Player, page); + } + else + { + args.Player.SendErrorMessage(GetString("无效的页码。请输入一个数字。")); + } + break; + + default: + Help(args); + break; + } + } + } + #endregion + + #region 菜单方法 + private static void Help(CommandArgs args) + { + if (args.Player.HasPermission("DonotFuck.admin")) + { + var mess = new StringBuilder(); + mess.AppendFormat(GetString("《禁止脏话》 [i:3456][C/F2F2C7:插件开发] [C/BFDFEA:by] [c/00FFFF:羽学][i:3459]\n")); + mess.AppendFormat(GetString("/df —— 查看菜单\n")); + mess.AppendFormat(GetString("/df list 页数 —— [c/B3B5EA:列出]所有[c/B3EADD:敏感词]\n")); + mess.AppendFormat(GetString("/df log —— [c/8AE072:开启]|[c/ED7985:关闭]敏感词[c/EBB4E2:记录]\n")); + mess.AppendFormat(GetString("/df clear —— [c/B3EADB:清理]所有[c/F2F191:脏话纪录]\n")); + mess.AppendFormat(GetString("/df add 或 del 词语 —— [c/87DF86:添加]|[c/F19092:删除]敏感词\n")); + mess.AppendFormat(GetString($"敏感词纪录:[c/B3B6EA:{Configuration.Instance.EnableLog}]")); + args.Player.SendMessage(mess.ToString(), 245, 241, 188); + } + else + { + var mess2 = new StringBuilder(); + mess2.AppendFormat(GetString("《禁止脏话》 [i:3456][C/F2F2C7:插件开发] [C/BFDFEA:by] [c/00FFFF:羽学][i:3459]\n")); + mess2.AppendFormat(GetString("/df —— 查看菜单\n")); + mess2.AppendFormat(GetString("/df list 页数 —— [c/B3B5EA:列出]所有[c/B3EADD:敏感词]")); + args.Player.SendMessage(mess2.ToString(), 245, 241, 188); + } + } + #endregion + + #region 辅助方法 用Linq查询并排列 列出敏感词 + private static void ListDirtyWords(TSPlayer plr, int page) + { + var Size = Configuration.Instance.PageSize; + var dirty = Configuration.Instance.DirtyWords.OrderBy(word => word).ToList(); + + var count = dirty.Count; + var pages = (int) Math.Ceiling(count / (double) Size); + + if (page < 1 || page > pages) + { + plr.SendErrorMessage(GetString("无效的页码。总共有 {0} 页。", pages)); + return; + } + + var start = (page - 1) * Size; + var end = Math.Min(start + Size, count); + + var words = dirty.Skip(start).Take(Size).Select((word, index) => $"{index + 1 + start}. {word}"); + + plr.SendInfoMessage(GetString($"《敏感词》第 {page} 页,共 {pages} 页:\n{string.Join("\n", words)}")); + } + #endregion +} diff --git a/src/DonotFuck/Configuration.cs b/src/DonotFuck/Configuration.cs index 5c38574b..d47ce2a5 100644 --- a/src/DonotFuck/Configuration.cs +++ b/src/DonotFuck/Configuration.cs @@ -1,85 +1,85 @@ -using Newtonsoft.Json; +using LazyAPI; +using LazyAPI.ConfigFiles; using TShockAPI; -namespace DonotFuck +namespace DonotFuck; + +[Config] +public class Configuration : JsonConfigBase { - public class Configuration - { - [JsonProperty("是否封禁")] - public bool Ban = true; //布尔型,只有开或关 + #region 实例变量 + [LocalizedPropertyName(CultureType.Chinese, "每页行数")] + [LocalizedPropertyName(CultureType.English, "pageLine")] + public int PageSize = 30; - [JsonProperty("封禁时长")] //用来在生成配置文件后对变量汉化 - public int ProhibitionTime = 10; //在DonotFuck类里,如果需要定义它,可以写成:Conifg.ProhibitionTime + [LocalizedPropertyName(CultureType.Chinese, "启用日志")] + [LocalizedPropertyName(CultureType.English, "enableLog")] + public bool EnableLog = true; - [JsonProperty("检查次数")] - public int InspectedQuantity = 5; //整数型,只有数字 + [LocalizedPropertyName(CultureType.Chinese, "脏话表")] + [LocalizedPropertyName(CultureType.English, "dirtyWords")] + public HashSet DirtyWords { get; set; } = new HashSet(); + #endregion - [JsonProperty("脏话表")] //提供一个哈希集合用于存储敏感词或脏话词汇。 - public HashSet DirtyWords { get; set; } = new HashSet(); + public const string _Directory = "DonotFuck"; - //存放配置文件的位置 - public static readonly string FilePath = Path.Combine(TShock.SavePath,"禁止脏话.json"); + private StreamWriter? writer; - // 添加构造函数,用Read方法引用进去 - public Configuration() - { - DirtyWords = new HashSet - { - "操", - "妈的", - "傻逼", - "煞笔", - "你妈", - "草你", - }; - } + internal StreamWriter Logger => this.writer ??= new StreamWriter(this.logFilePath); - // 将当前对象序列化为 JSON 格式,并以格式化的、可读的方式写入指定路径的文件中。 - public void Write(string path) - { - // 使用FileStream打开或创建指定路径的文件,设置模式为Create(如果文件不存在则创建,存在则覆盖),访问模式为Write,共享模式为Write,允许其他进程同时进行写入操作。 - using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write)) - { - // 使用JsonConvert将当前对象序列化为JSON字符串,并设置输出格式为缩进格式,提高可读性。 - var str = JsonConvert.SerializeObject(this, Formatting.Indented); + internal string logFilePath => Path.Combine(TShock.SavePath, _Directory, $"DonotFuck-{DateTime.Now:yyyy-MM-dd}.log"); - // 创建一个StreamWriter,将其与FileStream关联,以便将文本数据写入文件流。 - using (var sw = new StreamWriter(fs)) - { - // 使用StreamWriter将序列化后的JSON字符串写入到文件中。 - sw.Write(str); - } - } - } + protected override string Filename => Path.Combine(_Directory, "Config"); + #region 预设参数方法 + protected override void SetDefault() + { + this.DirtyWords = new HashSet { "6", "六" }; + } + #endregion - // 从指定路径读取配置文件。如果文件不存在,则创建一个默认配置文件并返回该默认配置。 - public static Configuration Read(string path) - { - // 检查指定路径的文件是否存在。 - if (!File.Exists(path)) - { - // 如果配置文件不存在,则创建一个新的默认配置实例。 - var defaultConfig = new Configuration(); // 使用默认构造函数初始化配置。 + #region 增删改方法 + internal bool Exempt(string text) + { + return this.DirtyWords.Contains(text); + } - // 将这个默认配置写入到指定路径,确保有配置文件可返回。 - defaultConfig.Write(path); + public void Log(string text) + { + if (this.EnableLog) + { + this.Logger.WriteLine(text); + this.Logger.Flush(); + } + } - // 返回新创建的默认配置。 - return defaultConfig; - } + public void DisposeLog() + { + this.writer?.Close(); + this.writer?.Dispose(); + this.writer = null; + } - // 文件存在时,使用FileStream以只读和文件共享读取模式打开文件。 - using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) - using (var sr = new StreamReader(fs)) // 使用StreamReader来读取文件内容。 - { - // 读取文件的全部内容,然后使用JsonConvert反序列化为Configuration对象。 - var jsonContent = sr.ReadToEnd(); - var cf = JsonConvert.DeserializeObject(jsonContent)!; + public bool Add(string text) + { + if (this.Exempt(text)) + { + return false; + } + this.DirtyWords.Add(text); + this.SaveTo(); + return true; + } - // 返回反序列化得到的Configuration对象。 - return cf; - } + public bool Del(string text) + { + if (this.Exempt(text)) + { + this.DirtyWords.Remove(text); + this.SaveTo(); + return true; } + return false; } + #endregion } \ No newline at end of file diff --git a/src/DonotFuck/DonotFuck.cs b/src/DonotFuck/DonotFuck.cs index 2336b353..06acfe15 100644 --- a/src/DonotFuck/DonotFuck.cs +++ b/src/DonotFuck/DonotFuck.cs @@ -1,169 +1,77 @@ -using Terraria; +using LazyAPI; +using System.Text.RegularExpressions; +using Terraria; using TerrariaApi.Server; using TShockAPI; -using TShockAPI.Hooks; - namespace DonotFuck; [ApiVersion(2, 1)] -public class Plugin : TerrariaPlugin +public class Plugin : LazyPlugin { - //作者名称 - public override string Author => "Cai 羽学修改"; - //插件的一句话描述 - public override string Description => "禁止脏话"; - //插件的名称 + #region 插件信息 public override string Name => "Don't Fuck"; - //插件的版本 - public override Version Version => new Version(2, 0, 1); - - internal static Configuration Config = null!; //将Config初始化 + public override string Author => "Cai 羽学"; + public override string Description => "当玩家聊天有敏感词时用*号代替该词"; + public override Version Version => new Version(3, 1, 0); + #endregion - - //插件的构造器 - public Plugin(Main game) : base(game) - { - // 实例化配置对象,用于存储和管理插件的配置信息。 - Config = new Configuration(); - - // 验证配置对象及其内部的敏感词集合是否已被正确初始化。 - // 如果任一条件未满足(即Config为null或者Config.DirtyWords为null),说明配置未正确完成, - // 这时抛出InvalidOperationException异常,提示开发者或系统管理员检查配置初始化逻辑。 - if (Config == null || Config.DirtyWords == null) - { - throw new InvalidOperationException("\n配置未正确初始化。"); - } - } - - - //插件加载时执行的代码 + #region 注册与释放 + public Plugin(Main game) : base(game) { } public override void Initialize() { - LoadConfig(); - ServerApi.Hooks.ServerChat.Register(this, this.OnChat); //注册聊天钩子 - - // 注册重新加载事件的监听器,当接收到重新加载信号时重新加载配置。 - GeneralHooks.ReloadEvent += LoadConfig; + ServerApi.Hooks.ServerChat.Register(this, this.OnChat); + TShockAPI.Commands.ChatCommands.Add(new Command("DonotFuck", Commands.DFCmd, "df")); } - // 重新加载配置文件的方法,支持在接收到重新加载事件或直接调用时更新配置。 - private static void LoadConfig(ReloadEventArgs? args = null) + protected override void Dispose(bool disposing) { - // 使用Read方法加载配置文件,若文件不存在则自动创建。 - Config = Configuration.Read(Configuration.FilePath); - - // 写回配置文件,这一步通常用于确保配置的持久化或格式化,读取方法 - Config.Write(Configuration.FilePath); - - // 如果ReloadEventArgs不为空且包含玩家信息,则向该玩家发送成功重新加载配置的消息。 - if (args != null && args.Player != null) + if (disposing) { - args.Player.SendSuccessMessage("[禁止脏话]重新加载配置完毕。"); + ServerApi.Hooks.ServerChat.Deregister(this, this.OnChat); + TShockAPI.Commands.ChatCommands.RemoveAll(x => x.CommandDelegate == Commands.DFCmd); } + base.Dispose(disposing); } + #endregion - // 检查玩家聊天行为 - private void OnChat(ServerChatEventArgs args) + public void ReplaceSensitiveWords(ref string text, out List SensitiveWords) { - var player = TShock.Players[args.Who]; - - if (player == null || player.HasPermission("Civilized") || player.Group.Name.Equals("owner", StringComparison.OrdinalIgnoreCase)) + SensitiveWords = new List(); + foreach (var bad in Configuration.Instance.DirtyWords) { - return; - } - var WordsCount = 0; - - // 遍历脏话列表,计算本次聊天触发的脏话数量 - foreach (var badWord in Config.DirtyWords) - { - if (args.Text.Contains(badWord, StringComparison.OrdinalIgnoreCase)) + if (text.Contains(bad, StringComparison.OrdinalIgnoreCase)) { - WordsCount++; - } - } - - // 如果触发了脏话,提醒玩家并更新累计违规次数 - if (WordsCount > 0) - { - var Text = args.Text; // 原始发言内容 - var BadWordList = new List(); // 存储玩家准确的脏话词语 - - // 遍历脏话表检查是否有匹配项 - foreach (var badWord in Config.DirtyWords) - { - if (Text.Contains(badWord, StringComparison.OrdinalIgnoreCase)) - { - BadWordList.AddRange(GetExactMatches(Text, badWord)); // 获取并添加精确匹配的脏话 - } - } - - // 如果有触发脏话,显示给玩家的信息 - if (BadWordList.Any()) - { - var ShowBadWords = ""; - foreach (var badWord in BadWordList) - { - ShowBadWords += $"- {badWord}\n"; - } - - //通知所有玩家 - TSPlayer.All.SendInfoMessage($"玩家[c/FFCCFF:{player.Name}]触发了以下敏感词:\n{ShowBadWords.TrimEnd('\n')}"); - - - foreach (var badWord in BadWordList) - { - // 输出准确的脏话词语到控制台 - TShock.Log.ConsoleInfo($"玩家 [{player.Name}] 发言中的脏话:{badWord}"); - } - } - - var Count = Ban.Trigger(player.Name); - // 只有累计违规次数达到上限才发送提醒信息并执行封禁逻辑 - if (Count > Config.InspectedQuantity) - { - TSPlayer.All.SendSuccessMessage($"玩家[c/FFCCFF:{player.Name}]被检测到多次用词不当!"); - Console.WriteLine($"玩家[{player.Name}]被检测到多次用词不当!"); - - // 达到违规次数上限后执行封禁逻辑 - if (Config.Ban) - { - //Ban类,触发封禁方法 - Ban.AddBan(player.Name, $"不许说脏话", Config.ProhibitionTime * 60); - //通知所有玩家 - TSPlayer.All.SendInfoMessage($"{player.Name}已被封禁!原因:连续说了脏话"); - //输出到日志文件 - TShock.Log.ConsoleInfo($"{player.Name}已被封禁!原因:连续说了脏话"); - //断开玩家链接,并通知原因 - player.Disconnect($"你已被封禁!原因:连续说了脏话!"); - - Ban.Remove(player.Name); //Ban类,清零玩家违规次数 - return; - } + var Replace = new string('*', bad.Length); // 创建与脏话等长的星号字符串 + text = Regex.Replace(text, bad, Replace, RegexOptions.IgnoreCase); // 替换脏话 + SensitiveWords.Add(bad); // 添加敏感词到列表 } } } - // 定义获取原始文本中精确匹配脏话的辅助函数 - private static IEnumerable GetExactMatches(string text, string badWord) + #region 检查玩家聊天行为 + private void OnChat(ServerChatEventArgs args) { - var index = 0; - while ((index = text.IndexOf(badWord, index, StringComparison.OrdinalIgnoreCase)) != -1) + var plr = TShock.Players[args.Who]; + + if (plr == null || plr.HasPermission("DonotFuck.admin") || plr.Group.Name == "owner") { - yield return text.Substring(index, badWord.Length); - index += badWord.Length; + return; } - } - - //释放钩子 - protected override void Dispose(bool disposing) - { - // 当disposing为true,表示是通过代码显式调用Dispose(),此时应执行额外的清理工作。 - if (disposing) + var Text = args.Text; + if (Text.StartsWith(TShock.Config.Settings.CommandSpecifier) || Text.StartsWith(TShock.Config.Settings.CommandSilentSpecifier)) + { + return; + } + this.ReplaceSensitiveWords(ref Text, out var sensitiveWords); + if (sensitiveWords.Any()) { - ServerApi.Hooks.ServerChat.Deregister(this, this.OnChat); //卸载聊天钩子 + TSPlayer.All.SendMessage(string.Format(TShock.Config.Settings.ChatFormat, plr.Group.Name, plr.Group.Prefix, plr.Name, plr.Group.Suffix, Text), plr.Group.R, plr.Group.G, plr.Group.B); + TShock.Log.ConsoleInfo(string.Format($"{plr.Name}:{args.Text}\n")); + TShock.Log.ConsoleInfo(GetString($"敏感词: {string.Join(",", sensitiveWords)}\n")); // 打印所有敏感词 + Configuration.Instance.Log($"[{DateTime.Now:u}] [{plr.Name}] 触发敏感词: {string.Join(",", sensitiveWords)}"); + args.Handled = true; } - // 调用基类的Dispose方法,以确保基类中可能存在的资源也得到正确释放。 - base.Dispose(disposing); } + #endregion } \ No newline at end of file diff --git a/src/DonotFuck/DonotFuck.csproj b/src/DonotFuck/DonotFuck.csproj index f489ab64..db42cf41 100644 --- a/src/DonotFuck/DonotFuck.csproj +++ b/src/DonotFuck/DonotFuck.csproj @@ -2,4 +2,8 @@ + + + + \ No newline at end of file diff --git a/src/DonotFuck/README.md b/src/DonotFuck/README.md index 4e7c3f22..97e7664a 100644 --- a/src/DonotFuck/README.md +++ b/src/DonotFuck/README.md @@ -1,33 +1,60 @@ ## DonotFuck 禁止脏话 -- 作者: Cai 羽学修改 -- 出处: TShock官方群 -- +- 作者: Cai 羽学 +- 出处: [禁止脏话(内嵌版)](https://github.com/1242509682/DonotFuck) +- 这是一个Tshock服务器插件主要用于:对服务器内的玩家检测文明发言,如果发送了配置文件中的字符则用*号代替该玩家词语。 ## 更新日志 ``` -v2.0.1 -先这样,晚点再重构 +v3.1 +补充了i18n 英文翻译 +加入了发言是否为指令的判断处理 +加入了记录敏感词到配置文件目录下的《脏话纪录》 +加入了/df 系列指令 +将指令权限划分 +DonotFuck:用于给玩家查询敏感词 +DonotFuck.admin:用于给管理员添加或者移除敏感词 + +v3.0 +重构代码逻辑,移除了封禁逻辑,当检测玩家有脏话时用*号代替 +将免检的权限名更改为:DonotFuck + +v2.0 +1.修复了玩家被封禁后不会清空计数问题 +2.修复了玩家随意发送字符1次都会导致封禁问题 +3.修复了空引用,引起的聊天黄码刷屏问题 +4.修复了玩家每次发脏话不会广播问题, +并列出所触发的敏感词,输出给所有玩家与控制台 +5.添加了日志记录 +6.移除了在Config中的【启用】选项 + +v1.0: +1.可以用配置文件自定义:启用开关、词、封禁时长、是否封禁、检查次数 +2.给配置文件加了Reload重载方法 + ``` + ## 指令 -无 +| 语法 | 别名 | 权限 | 说明 | +| -------------------------------- | :---: | :--------------: | :--------------------------------------: | +| /df | 无 | DonotFuck | 指令菜单 | +| /df list | 无 | DonotFuck | 列出敏感词表(给玩家用的) | +| /df log | 无 | DonotFuck.admin | 开启或关闭敏感词记录 | +| /df add 词语 | 无 | DonotFuck.admin | 添加敏感词 | +| /df del 词语 | 无 | DonotFuck.admin | 移除敏感词 | +| /reload | 无 | tshock.cfg.reload | 重载配置文件 | ## 配置 -> 配置文件路径: tshock/禁止脏话.json +> 配置文件路径: tshock/禁止脏话/禁止脏话.json ```json { - "是否封禁": true, - "封禁时长": 10, - "检查次数": 5, + "每页行数": 30, + "记录日志": true, "脏话表": [ - "操", - "妈的", - "傻逼", - "煞笔", - "你妈", - "草你" + "6", + "六" ] } ``` diff --git a/src/DonotFuck/README_EN.md b/src/DonotFuck/README_EN.md new file mode 100644 index 00000000..4cd9738f --- /dev/null +++ b/src/DonotFuck/README_EN.md @@ -0,0 +1,66 @@ +# DonotFuck + +- Authors: 羽学 & Cai +- Source: [禁止脏话(内嵌版)](https://github.com/1242509682/DonotFuck) +- This is a Tshock server plugin primarily used for: detecting civilized speech among players on the server. +- If a player sends characters listed in the configuration file, those words will be replaced with asterisks (*). + +## Update Log + +``` +v3.1 +- Added i18n English translation +- Added handling to determine if the message is a command +- Added logging of sensitive words to the `Swear Record` file under the configuration directory +- Added `/df` series commands +- Divided command permissions +- `DonotFuck`: Used for players to query sensitive words +- `DonotFuck.admin`: Used for administrators to add or remove sensitive words + +v3.0 +- Refactored code logic, removed banning logic. When a player uses a swear word, it will be replaced with asterisks (*). +- Changed the permission name for exempted players to `DonotFuck`. + +v2.0 +1. Fixed the issue where the count was not reset after a player was banned +2. Fixed the issue where sending any character once would result in a ban +3. Fixed the yellow chat spam caused by null reference exceptions +4. Fixed the issue where each time a player sent a swear word, it would not be broadcasted + - The triggered sensitive words are now listed and output to all players and the console +5. Added logging +6. Removed the ["Enable"] option in the Config + +v1.0: +1. Added the ability to customize the following via the configuration file: enable switch, words, ban duration, whether to ban, and check frequency +2. Added a Reload method for the configuration file + +``` + +## Commands + +| Command Syntax | Alias | Permission | Description | +| -------------------------------- | :---: | :--------------: | :--------------------------------------: | +| /af | None | DonotFuck | Command menu | +| /af list | None | DonotFuck | List sensitive words (for players) | +| /af log | None | DonotFuck.admin | Enable or disable sensitive word logging | +| /af add | None | DonotFuck.admin | Add a sensitive word | +| /af del | None | DonotFuck.admin | Remove a sensitive word | +| /reload | None | tshock.cfg.reload | Reload the configuration file | + +## Configuration +> Configuration file location: tshock/禁止脏话/禁止脏话.json +```json +{ + "RowsPerPage": 30, + "LogEnabled": true, + "SensitiveWords": [ + "6", + "六" + ] +} +``` + +## FeedBack +- Github Issue -> TShockPlugin Repo: https://github.com/UnrealMultiple/TShockPlugin +- TShock QQ Group: 816771079 +- China Terraria Forum: trhub.cn, bbstr.net, tr.monika.love diff --git a/src/DonotFuck/i18n/en-US.po b/src/DonotFuck/i18n/en-US.po new file mode 100644 index 00000000..ed971daa --- /dev/null +++ b/src/DonotFuck/i18n/en-US.po @@ -0,0 +1,115 @@ +msgid "" +msgstr "" +"Project-Id-Version: DonotFuck\n" +"POT-Creation-Date: 2024-11-22 16:04:17+0800\n" +"PO-Revision-Date: 2024-11-22 16:06+0800\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: en_US\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.5\n" + +#: ..\..\DonotFuck.cs:51 +msgid "[禁止脏话]重新加载配置完毕。" +msgstr "[Don't Fuck]Configuration reloaded。" + +#: ..\..\Commands.cs:89 +#, csharp-format +msgid "[c/92C5EC:{0}] 不在敏感词表中!" +msgstr "[c/92C5EC:{0}] Not in the Dirty Words List!" + +#: ..\..\Commands.cs:75 +#, csharp-format +msgid "[c/92C5EC:{0}] 已在敏感词表中!" +msgstr "[c/92C5EC:{0}] Already in the Dirty Words List!" + +#: ..\..\Commands.cs:121 ..\..\Commands.cs:133 +msgid "" +"《禁止脏话》 [i:3456][C/F2F2C7:插件开发] [C/BFDFEA:by] [c/00FFFF:羽学]" +"[i:3459]\n" +msgstr "" +"《Don't Fuck》 [i:3456][C/F2F2C7:Plugin Development] [C/BFDFEA:by] [c/00FFFF:" +"Yu Xue][i:3459]\n" + +#: ..\..\Commands.cs:161 +#, csharp-format +msgid "" +"《敏感词》第 {0} 页,共 {1} 页:\n" +"{2}" +msgstr "" +"《SensitiveWords》Page {0}, Total Pages {1}:\n" +"{2}" + +#: ..\..\Commands.cs:48 +msgid "《脏话纪录》文件夹已成功清空。" +msgstr "《Swear Record》Folder has been successfully cleared。" + +#: ..\..\Commands.cs:122 ..\..\Commands.cs:134 +msgid "/df —— 查看菜单\n" +msgstr "/df —— View Menu\n" + +#: ..\..\Commands.cs:126 +msgid "/df add 或 del 词语 —— [c/87DF86:添加]|[c/F19092:删除]敏感词\n" +msgstr "" +"/df add | del [Text] —— [c/87DF86:Add]|[c/F19092:Delete] Sensitive Words\n" + +#: ..\..\Commands.cs:125 +msgid "/df clear —— [c/B3EADB:清理]所有[c/F2F191:脏话纪录]\n" +msgstr "/df clear —— [c/B3EADB:Clear] all [c/F2F191:Swear Records]\n" + +#: ..\..\Commands.cs:135 +msgid "/df list 页数 —— [c/B3B5EA:列出]所有[c/B3EADD:敏感词]" +msgstr "/df list [Page] —— [c/B3B5EA:List] all [c/B3EADD:Sensitive Words]" + +#: ..\..\Commands.cs:123 +msgid "/df list 页数 —— [c/B3B5EA:列出]所有[c/B3EADD:敏感词]\n" +msgstr "/df list [Page] —— [c/B3B5EA:List] all [c/B3EADD:Sensitive Words]\n" + +#: ..\..\Commands.cs:124 +msgid "/df log —— [c/8AE072:开启]|[c/ED7985:关闭]敏感词[c/EBB4E2:记录]\n" +msgstr "" +"/df log —— [c/8AE072:Enable]|[c/ED7985:Disable] Sensitive Word [c/EBB4E2:" +"Logging]\n" + +#: ..\..\Commands.cs:30 +msgid "禁用" +msgstr "Disable" + +#: ..\..\DonotFuck.cs:99 +#, csharp-format +msgid "敏感词: {0}\n" +msgstr "Sensitive Words: {0}\n" + +#: ..\..\Commands.cs:127 +#, csharp-format +msgid "敏感词纪录:[c/B3B6EA:{0}]" +msgstr "Sensitive Word Record: [c/B3B6EA:{0}]" + +#: ..\..\Commands.cs:31 +msgid "启用" +msgstr "Enable" + +#: ..\..\Commands.cs:53 +msgid "请输入指令开启敏感词记录:/df log" +msgstr "Please enter the command to enable sensitive word logging: /df log" + +#: ..\..\Commands.cs:103 +msgid "无效的页码。请输入一个数字。" +msgstr "Invalid page number. Please enter a number。" + +#: ..\..\Commands.cs:152 +#, csharp-format +msgid "无效的页码。总共有 {0} 页。" +msgstr "Invalid page number. There are {0} pages in total。" + +#: ..\..\Commands.cs:85 +#, csharp-format +msgid "已成功将敏感词从表中移除: [c/92C5EC:{0}]!" +msgstr "Successfully removed the sensitive word from the list: [c/92C5EC:{0}]!" + +#: ..\..\Commands.cs:71 +#, csharp-format +msgid "已成功将敏感词添加到表中: [c/92C5EC:{0}]!" +msgstr "Successfully added the sensitive word to the list: [c/92C5EC:{0}]!" diff --git a/src/DonotFuck/i18n/template.pot b/src/DonotFuck/i18n/template.pot new file mode 100644 index 00000000..2900e8f5 --- /dev/null +++ b/src/DonotFuck/i18n/template.pot @@ -0,0 +1,115 @@ +msgid "" +msgstr "" +"Project-Id-Version: DonotFuck\n" +"POT-Creation-Date: 2024-11-22 16:04:17+0800\n" +"PO-Revision-Date: 2024-11-22 16:04:17+0800\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: GetText.NET Extractor\n" + +#: ..\..\DonotFuck.cs:51 +msgid "[禁止脏话]重新加载配置完毕。" +msgstr "" + +#: ..\..\Commands.cs:89 +#, csharp-format +msgid "[c/92C5EC:{0}] 不在敏感词表中!" +msgstr "" + +#: ..\..\Commands.cs:75 +#, csharp-format +msgid "[c/92C5EC:{0}] 已在敏感词表中!" +msgstr "" + +#: ..\..\Commands.cs:121 +#: ..\..\Commands.cs:133 +msgid "" +"《禁止脏话》 [i:3456][C/F2F2C7:插件开发] [C/BFDFEA:by] [c/00FFFF:羽学][i:3459]\n" +msgstr "" + +#: ..\..\Commands.cs:161 +#, csharp-format +msgid "" +"《敏感词》第 {0} 页,共 {1} 页:\n" +"{2}" +msgstr "" + +#: ..\..\Commands.cs:48 +msgid "《脏话纪录》文件夹已成功清空。" +msgstr "" + +#: ..\..\Commands.cs:122 +#: ..\..\Commands.cs:134 +msgid "" +"/df —— 查看菜单\n" +msgstr "" + +#: ..\..\Commands.cs:126 +msgid "" +"/df add 或 del 词语 —— [c/87DF86:添加]|[c/F19092:删除]敏感词\n" +msgstr "" + +#: ..\..\Commands.cs:125 +msgid "" +"/df clear —— [c/B3EADB:清理]所有[c/F2F191:脏话纪录]\n" +msgstr "" + +#: ..\..\Commands.cs:135 +msgid "/df list 页数 —— [c/B3B5EA:列出]所有[c/B3EADD:敏感词]" +msgstr "" + +#: ..\..\Commands.cs:123 +msgid "" +"/df list 页数 —— [c/B3B5EA:列出]所有[c/B3EADD:敏感词]\n" +msgstr "" + +#: ..\..\Commands.cs:124 +msgid "" +"/df log —— [c/8AE072:开启]|[c/ED7985:关闭]敏感词[c/EBB4E2:记录]\n" +msgstr "" + +#: ..\..\Commands.cs:30 +msgid "禁用" +msgstr "" + +#: ..\..\DonotFuck.cs:99 +#, csharp-format +msgid "" +"敏感词: {0}\n" +msgstr "" + +#: ..\..\Commands.cs:127 +#, csharp-format +msgid "敏感词纪录:[c/B3B6EA:{0}]" +msgstr "" + +#: ..\..\Commands.cs:31 +msgid "启用" +msgstr "" + +#: ..\..\Commands.cs:53 +msgid "请输入指令开启敏感词记录:/df log" +msgstr "" + +#: ..\..\Commands.cs:103 +msgid "无效的页码。请输入一个数字。" +msgstr "" + +#: ..\..\Commands.cs:152 +#, csharp-format +msgid "无效的页码。总共有 {0} 页。" +msgstr "" + +#: ..\..\Commands.cs:85 +#, csharp-format +msgid "已成功将敏感词从表中移除: [c/92C5EC:{0}]!" +msgstr "" + +#: ..\..\Commands.cs:71 +#, csharp-format +msgid "已成功将敏感词添加到表中: [c/92C5EC:{0}]!" +msgstr "" + diff --git a/src/DwTP/DwTP.cs b/src/DwTP/DwTP.cs new file mode 100644 index 00000000..0413506a --- /dev/null +++ b/src/DwTP/DwTP.cs @@ -0,0 +1,242 @@ +using Terraria; +using TerrariaApi.Server; +using TShockAPI; + +namespace DwTP; + +[ApiVersion(2, 1)] +public class dwTP : TerrariaPlugin +{ + #region 插件信息 + public override string Name => "定位传送"; + public override string Author => "羽学"; + public override Version Version => new Version(1, 0, 0); + public override string Description => "用/dw命令传送到微光湖、地牢、神庙、花苞、宝藏袋位置"; + #endregion + + #region 注册与释放 + public dwTP(Main game) : base(game) { } + + public override void Initialize() + { + Commands.ChatCommands.Add(new Command("dw.use", this.dwCmd, "dw", "定位")); + ServerApi.Hooks.NpcKilled.Register(this, this.OnNpcKilled); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + Commands.ChatCommands.RemoveAll(cmd => cmd.CommandDelegate == this.dwCmd); + ServerApi.Hooks.NpcKilled.Deregister(this, this.OnNpcKilled); + } + base.Dispose(disposing); + } + + #endregion + + #region Boss死亡记录宝藏袋位置 + private bool Bag = false; + private int BagX = 0; + private int BagY = 0; + private void OnNpcKilled(NpcKilledEventArgs e) + { + if (e.npc.boss) + { + this.Bag = true; + } + + if (this.Bag) + { + this.BagX = (int)e.npc.position.X; + this.BagY = (int)e.npc.position.Y; + } + } + #endregion + + #region 指令方法 + private void dwCmd(CommandArgs args) + { + if (args.Parameters.Count == 0) + { + args.Player.SendMessage(GetString($"[i:3455][c/AD89D5:定][c/D68ACA:位][c/DF909A:传][c/E5A894:送][i:3454] [C/BFDFEA:by] [c/7CAEDD:羽学][i:3459]\n") + + GetString($"/dw wg ——传送到[c/E2E4C4:微光湖]附近\n") + + GetString($"/dw sm ——传送到[c/E2E4C4:神庙]附近\n") + + GetString($"/dw hb ——传送到[c/E2E4C4:花苞]附近\n") + + GetString($"/dw bag ——传送到[c/E2E4C4:宝藏袋]附近\n") + + GetString($"/dw dl ——传送到[c/E2E4C4:地牢]附近[骷髅王前/石后]"), 247, 244, 150); + } + + else if (args.Parameters.Count == 1 && (args.Parameters[0] == "wg" || args.Parameters[0] == "微光")) + { + var flag = false; + var x2 = 0; + var y2 = 0; + + if (!flag) + { + for (var x = 0; x < Main.maxTilesX; x++) + { + for (var y = 0; y < Main.maxTilesY; y++) + { + var tile = Main.tile[x, y]; + if (tile != null && tile.liquidType() == Terraria.ID.LiquidID.Shimmer) + { + x2 = x; + y2 = y - 3; + + if (!tile.active()) + { + WorldGen.PlaceTile(x, y, 38, false, true, -1, 0); + } + + flag = true; + break; + } + } + } + } + + if (flag) + { + args.Player.SendMessage(GetString($"已将您传送到[c/F25156:微光湖]附近([c/E2E4C4:{x2} {y2}])"), 222, 192, 223); + args.Player.Teleport(x2 * 16, y2 * 16, 10); + } + return; + } + + else if (args.Parameters.Count == 1 && (args.Parameters[0] == "sm" || args.Parameters[0] == "神庙")) + { + var flag = false; + var x2 = 0; + var y2 = 0; + + if (!flag) + { + for (var x = 0; x < Main.maxTilesX; x++) + { + for (var y = 0; y < Main.maxTilesY; y++) + { + var tile = Main.tile[x, y]; + if (tile == null) + { + return; + } + + if (tile.type == Terraria.ID.TileID.ClosedDoor && tile.wall == Terraria.ID.WallID.LihzahrdBrickUnsafe) + { + x2 = x + 6; + y2 = y - 3; + flag = true; + break; + } + else if (tile.type == 237) + { + x2 = x + 6; + y2 = y - 3; + flag = true; + break; + } + } + } + } + + if (flag) + { + args.Player.SendMessage(GetString($"已将您传送到[c/F25156:神庙]附近([c/E2E4C4:{x2} {y2}])"), 222, 192, 223); + args.Player.Teleport(x2 * 16, y2 * 16, 10); + } + return; + } + + else if (args.Parameters.Count == 1 && (args.Parameters[0] == "hb" || args.Parameters[0] == "花苞")) + { + var flag = false; + var x2 = 0; + var y2 = 0; + + if (!flag) + { + for (var x = 0; x < Main.maxTilesX; x++) + { + for (var y = 0; y < Main.maxTilesY; y++) + { + var tile = Main.tile[x, y]; + if (tile != null && tile.type == 238) + { + x2 = x; + y2 = y - 3; + flag = true; + break; + } + } + } + } + + if (flag) + { + args.Player.SendMessage(GetString($"已将您传送到[c/F25156:花苞]附近([c/E2E4C4:{x2} {y2}])"), 222, 192, 223); + args.Player.Teleport(x2 * 16, y2 * 16, 10); + } + else + { + args.Player.SendMessage(GetString($"世纪之花[c/F25156:未生长],无法获取[c/E2E4C4:花苞坐标]"), 222, 192, 223); + } + return; + } + + else if (args.Parameters.Count == 1 && (args.Parameters[0] == "dl" || args.Parameters[0] == "地牢")) + { + var flag = false; + var npcY = 0; + var npcX = 0; + if (!flag) + { + for (var i = 0; i < Main.npc.Length; i++) + { + var npc = Main.npc[i]; + if (npc.active) + { + if (npc.type == 37) + { + npcY = (int)npc.position.Y; + npcX = (int)npc.position.X; + flag = true; + break; + } + else if(npc.type == 438) + { + npcY = (int)npc.position.Y; + npcX = (int)npc.position.X; + flag = true; + break; + } + } + } + } + + if (flag) + { + args.Player.SendMessage(GetString($"已将您传送到[c/F25156:地牢]附近([c/E2E4C4:{npcX / 16} {npcY / 16}])"), 222, 192, 223); + args.Player.Teleport(npcX, npcY, 10); + } + else + { + args.Player.SendMessage(GetString($"地牢守护者[c/F25156:已击败],无法获取[c/E2E4C4:地牢坐标]"), 222, 192, 223); + } + + return; + } + + else if (args.Parameters.Count == 1 && (args.Parameters[0] == "bag" || args.Parameters[0] == "宝藏袋")) + { + if (this.Bag) + { + args.Player.SendMessage(GetString($"已将您传送到[c/F25156:宝藏袋]附近([c/E2E4C4:{this.BagX / 16} {this.BagY / 16}])"), 222, 192, 223); + args.Player.Teleport(this.BagX, this.BagY, 10); + } + return; + } + } + #endregion +} \ No newline at end of file diff --git a/src/DwTP/DwTP.csproj b/src/DwTP/DwTP.csproj new file mode 100644 index 00000000..f489ab64 --- /dev/null +++ b/src/DwTP/DwTP.csproj @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/DwTP/README.md b/src/DwTP/README.md new file mode 100644 index 00000000..2512e611 --- /dev/null +++ b/src/DwTP/README.md @@ -0,0 +1,38 @@ +# DwTP 定位传送插件 + +- 作者: 羽学 +- 出处: 无 +- 这是一个Tshock服务器插件,主要用于:使用指令定位传送微光湖、地牢、神庙、世纪之花花苞、BOSS宝藏袋 + +## 更新日志 + +``` +v1.0.0 +使用/dw指令传送到定位地标 +定位花苞:只在世界图格上存在花苞时才会传送 +定位地牢:只在世界存在地牢老人或邪教徒弓箭手时才会传送 +定位神庙:神庙门没打开时传送到门前,打开后传送到丛林蜥蜴祭坛 +定位宝藏袋:只在有BOSS死亡后才会传送到其死亡地点 +定位微光湖:传送到微光液体,第一次会判断液体上方没有方块时放置灰砖 +``` + +## 指令 + +| 语法 | 别名 | 权限 | 说明 | +| -------------------------------- | :---: | :--------------: | :--------------------------------------: | +| /dw | /定位 | dw.use | 指令菜单 | +| /dw hb | /定位 花苞 | dw.use | 传送到世纪之花花苞 | +| /dw dl | /定位 地牢 | dw.use | 传送到地牢老人或邪教徒弓箭手 | +| /dw sm | /定位 神庙 | dw.use | 传送到神庙门前或丛林蜥蜴祭坛 | +| /dw bag | /定位 宝藏袋 | dw.use | 传送到BOSS死亡地点(宝藏袋) | +| /dw wgh | /定位 微光湖 | dw.use | 传送到微光湖(并在液体上方为空时放置灰砖) | + + +## 配置 +```json +暂无 +``` +## 反馈 +- 优先发issued -> 共同维护的插件库:https://github.com/UnrealMultiple/TShockPlugin +- 次优先:TShock官方群:816771079 +- 大概率看不到但是也可以:国内社区trhub.cn ,bbstr.net , tr.monika.love \ No newline at end of file diff --git a/src/DwTP/README_EN.md b/src/DwTP/README_EN.md new file mode 100644 index 00000000..ab0b5539 --- /dev/null +++ b/src/DwTP/README_EN.md @@ -0,0 +1,38 @@ +# DwTP + +- Authors: 羽学 +- Source: 无 +- This is a Tshock server plugin, mainly used for: +- teleporting to specific landmarks using commands such as Glow Lakes, Dungeons, Temples, Plantera's Bulb, and Boss Loot Bags. + +## Update Log + +``` +v1.0.0 +Use the /dw command to teleport to designated landmarks. +- Bulb: Teleports only dw the bulb exists on the world map. +- Dungeon: Teleports only dw there is a Guide or Cultist Archer in the dungeon. +- Temple: Teleports to the entrance if the door is not open, otherwise teleports to the altar. +- Loot Bag: Teleports to the location of the Boss's death (loot bag). +- Glow Lake: Teleports to the glow liquid; places a gray brick above the liquid if it is empty the first time. +``` + +## Commands + +| Syntax | Alias | Permission | Description | +| -------------------------------- | :---: | :--------------: | :--------------------------------------: | +| /dw | /定位 | dw.use | Command menu | +| /dw hb | /定位 花苞 | dw.use | Teleport to Plantera's Bulb | +| /dw dl | /定位 地牢 | dw.use | Teleport to the Guide or Cultist Archer in the dungeon | +| /dw sm | /定位 神庙 | dw.use | Teleport to the temple entrance or the Jungle Shrine | +| /dw bag | /定位 宝藏袋 | dw.use | Teleport to the Boss's death location (loot bag) | +| /dw wgh | /定位 微光湖 | dw.use | Teleport to the Glow Lake (places a gray brick above the liquid if it is empty the first time) | + +## Configuration +```json +None +``` +## FeedBack +- Github Issue -> TShockPlugin Repo: https://github.com/UnrealMultiple/TShockPlugin +- TShock QQ Group: 816771079 +- China Terraria Forum: trhub.cn, bbstr.net, tr.monika.love \ No newline at end of file diff --git a/src/DwTP/i18n/en-US.po b/src/DwTP/i18n/en-US.po new file mode 100644 index 00000000..f9bfd6c1 --- /dev/null +++ b/src/DwTP/i18n/en-US.po @@ -0,0 +1,85 @@ +msgid "" +msgstr "" +"Project-Id-Version: DwTP\n" +"POT-Creation-Date: 2024-11-22 16:24:46+0800\n" +"PO-Revision-Date: 2024-11-22 16:29+0800\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: en_US\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.5\n" + +#: ..\..\DwTP.cs:62 +msgid "" +"[i:3455][c/AD89D5:定][c/D68ACA:位][c/DF909A:传][c/E5A894:送]" +"[i:3454] [C/BFDFEA:by] [c/7CAEDD:羽学][i:3459]\n" +msgstr "" +"[i:3455][c/AD89D5:Tele][c/D68ACA:port][c/DF909A:to][c/E5A894:" +"Location][i:3454] [C/BFDFEA:by] [c/7CAEDD:Yu Xue][i:3459]\n" + +#: ..\..\DwTP.cs:66 +msgid "/dw bag ——传送到[c/E2E4C4:宝藏袋]附近\n" +msgstr "/dw bag —— Teleport to near the [c/E2E4C4:LootBag]\n" + +#: ..\..\DwTP.cs:67 +msgid "/dw dl ——传送到[c/E2E4C4:地牢]附近[骷髅王前/石后]" +msgstr "" +"/dw dl —— Teleport to near the [c/E2E4C4:Dungeon] [Before " +"Skeletron / After Stone Giant]" + +#: ..\..\DwTP.cs:65 +msgid "/dw hb ——传送到[c/E2E4C4:花苞]附近\n" +msgstr "/dw hb —— Teleport to near the [c/E2E4C4:Bulb]\n" + +#: ..\..\DwTP.cs:64 +msgid "/dw sm ——传送到[c/E2E4C4:神庙]附近\n" +msgstr "/dw sm —— Teleport to near the [c/E2E4C4:Temple]\n" + +#: ..\..\DwTP.cs:63 +msgid "/dw wg ——传送到[c/E2E4C4:微光湖]附近\n" +msgstr "/dw wg —— Teleport to near the [c/E2E4C4:Glowing Lake]\n" + +#: ..\..\DwTP.cs:225 +msgid "地牢守护者[c/F25156:已击败],无法获取[c/E2E4C4:地牢坐标]" +msgstr "" +"Dungeon Guardian [c/F25156:defeated], cannot obtain [c/E2E4C4:" +"Dungeon coordinates]" + +#: ..\..\DwTP.cs:183 +msgid "世纪之花[c/F25156:未生长],无法获取[c/E2E4C4:花苞坐标]" +msgstr "" +"Plantera [c/F25156:not grown], cannot obtain [c/E2E4C4:Bulb " +"coordinates]" + +#: ..\..\DwTP.cs:235 +#, csharp-format +msgid "已将您传送到[c/F25156:宝藏袋]附近([c/E2E4C4:{0} {1}])" +msgstr "" +"Teleported you to near the [c/F25156:LootBag] ([c/E2E4C4:{0} {1}])" + +#: ..\..\DwTP.cs:220 +#, csharp-format +msgid "已将您传送到[c/F25156:地牢]附近([c/E2E4C4:{0} {1}])" +msgstr "" +"Teleported you to near the [c/F25156:Dungeon] ([c/E2E4C4:{0} {1}])" + +#: ..\..\DwTP.cs:178 +#, csharp-format +msgid "已将您传送到[c/F25156:花苞]附近([c/E2E4C4:{0} {1}])" +msgstr "" +"Teleported you to near the [c/F25156:Bulb] ([c/E2E4C4:{0} {1}])" + +#: ..\..\DwTP.cs:146 +#, csharp-format +msgid "已将您传送到[c/F25156:神庙]附近([c/E2E4C4:{0} {1}])" +msgstr "" +"Teleported you to near the [c/F25156:Temple] ([c/E2E4C4:{0} {1}])" + +#: ..\..\DwTP.cs:102 +#, csharp-format +msgid "已将您传送到[c/F25156:微光湖]附近([c/E2E4C4:{0} {1}])" +msgstr "" +"Teleported you to near the [c/F25156:Glowing Lake] ([c/E2E4C4:{0} " +"{1}])" diff --git a/src/DwTP/i18n/template.pot b/src/DwTP/i18n/template.pot new file mode 100644 index 00000000..acd0891f --- /dev/null +++ b/src/DwTP/i18n/template.pot @@ -0,0 +1,75 @@ +msgid "" +msgstr "" +"Project-Id-Version: DwTP\n" +"POT-Creation-Date: 2024-11-22 16:24:46+0800\n" +"PO-Revision-Date: 2024-11-22 16:24:46+0800\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: GetText.NET Extractor\n" + +#: ..\..\DwTP.cs:62 +msgid "" +"[i:3455][c/AD89D5:定][c/D68ACA:位][c/DF909A:传][c/E5A894:送][i:3454] " +"[C/BFDFEA:by] [c/7CAEDD:羽学][i:3459]\n" +msgstr "" + +#: ..\..\DwTP.cs:66 +msgid "" +"/dw bag ——传送到[c/E2E4C4:宝藏袋]附近\n" +msgstr "" + +#: ..\..\DwTP.cs:67 +msgid "/dw dl ——传送到[c/E2E4C4:地牢]附近[骷髅王前/石后]" +msgstr "" + +#: ..\..\DwTP.cs:65 +msgid "" +"/dw hb ——传送到[c/E2E4C4:花苞]附近\n" +msgstr "" + +#: ..\..\DwTP.cs:64 +msgid "" +"/dw sm ——传送到[c/E2E4C4:神庙]附近\n" +msgstr "" + +#: ..\..\DwTP.cs:63 +msgid "" +"/dw wg ——传送到[c/E2E4C4:微光湖]附近\n" +msgstr "" + +#: ..\..\DwTP.cs:225 +msgid "地牢守护者[c/F25156:已击败],无法获取[c/E2E4C4:地牢坐标]" +msgstr "" + +#: ..\..\DwTP.cs:183 +msgid "世纪之花[c/F25156:未生长],无法获取[c/E2E4C4:花苞坐标]" +msgstr "" + +#: ..\..\DwTP.cs:235 +#, csharp-format +msgid "已将您传送到[c/F25156:宝藏袋]附近([c/E2E4C4:{0} {1}])" +msgstr "" + +#: ..\..\DwTP.cs:220 +#, csharp-format +msgid "已将您传送到[c/F25156:地牢]附近([c/E2E4C4:{0} {1}])" +msgstr "" + +#: ..\..\DwTP.cs:178 +#, csharp-format +msgid "已将您传送到[c/F25156:花苞]附近([c/E2E4C4:{0} {1}])" +msgstr "" + +#: ..\..\DwTP.cs:146 +#, csharp-format +msgid "已将您传送到[c/F25156:神庙]附近([c/E2E4C4:{0} {1}])" +msgstr "" + +#: ..\..\DwTP.cs:102 +#, csharp-format +msgid "已将您传送到[c/F25156:微光湖]附近([c/E2E4C4:{0} {1}])" +msgstr "" +