From 20a352e936ad8d840b8d575f63220651fdf9e926 Mon Sep 17 00:00:00 2001 From: stardream <1011819146@qq.com> Date: Sat, 27 Jun 2026 18:44:40 +0800 Subject: [PATCH 1/7] =?UTF-8?q?v2.8.0:=20=E6=B7=BB=E5=8A=A0RealTime?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=88=B0src=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/RealTime/RealTime.cs | 708 ++++++++++++++++++++++++++++----------- 1 file changed, 507 insertions(+), 201 deletions(-) diff --git a/src/RealTime/RealTime.cs b/src/RealTime/RealTime.cs index 914e5230b..b381e33da 100644 --- a/src/RealTime/RealTime.cs +++ b/src/RealTime/RealTime.cs @@ -1,25 +1,61 @@ -using Terraria; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using Terraria; using TerrariaApi.Server; using TShockAPI; +using TShockAPI.Hooks; namespace RealTime; -public delegate void ChangeCloud(); [ApiVersion(2, 1)] public class RealTime : TerrariaPlugin { - public override string Author => "十七"; - public override string Description => GetString("同步现实时间"); - public override string Name => System.Reflection.Assembly.GetExecutingAssembly().GetName().Name!; - public override Version Version => new Version(2, 6, 0, 5); + #region 基础部分 + public override string Author => "十七、星梦"; + public override string Description => "同步现实时间"; + public override string Name => Assembly.GetExecutingAssembly().GetName().Name!; + public override Version Version => new Version(2, 8, 0, 0); + private static readonly Random rand = new Random(); + + private static readonly string ConfigPath = Path.Combine(TShock.SavePath, "realtime.json"); + public static RealTimeConfig Config { get; private set; } = null!; + + private int realTimeSyncTimer = 0; + private int npcWeatherTimer = 0; + private int weatherChangeTimer = 0; + private int secondTimer = 0; + + private int bloodMoonSecondsLeft = 0; + private int eclipseSecondsLeft = 0; + private int pumpkinMoonSecondsLeft = 0; + private int snowMoonSecondsLeft = 0; + + private bool lastBloodMoon = false; + private bool lastEclipse = false; + private bool lastPumpkinMoon = false; + private bool lastSnowMoon = false; + public RealTime(Main game) : base(game) { } + public override void Initialize() { + Config = RealTimeConfig.Load(ConfigPath); + ServerApi.Hooks.GameUpdate.Register(this, this.OnGameUpdate); On.Terraria.Main.UpdateTime += this.NPCS; GetDataHandlers.PlayerTeam += this.Team; + On.Terraria.WorldGen.UnspawnTravelNPC += this.BlockUnspawn; + GeneralHooks.ReloadEvent += this.OnReload; + + Commands.ChatCommands.Add(new Command("realtime.admin", this.CmdRte, "rte") + { + HelpText = "查看和修改RealTime插件配置。用法: /rte [序列号] [值]" + }); } protected override void Dispose(bool disposing) @@ -29,274 +65,544 @@ protected override void Dispose(bool disposing) ServerApi.Hooks.GameUpdate.Deregister(this, this.OnGameUpdate); On.Terraria.Main.UpdateTime -= this.NPCS; GetDataHandlers.PlayerTeam -= this.Team; + On.Terraria.WorldGen.UnspawnTravelNPC -= this.BlockUnspawn; + GeneralHooks.ReloadEvent -= this.OnReload; } base.Dispose(disposing); } + #endregion + + #region 指令系统 + private static readonly Dictionary ConfigIndexMap = new() + { + { 1, "同步现实时间" }, + { 2, "同步间隔秒" }, + { 3, "事件自动结束" }, + { 4, "血月日食持续时间分钟" }, + { 5, "霜月万圣节持续时间分钟" }, + { 6, "渔夫任务刷新" }, + { 7, "月相刷新" }, + { 8, "NPC综合刷新间隔分钟" }, + { 9, "老人自动召唤" }, + { 10, "拜月教徒自动召唤" }, + { 11, "旅商商品刷新" }, + { 12, "旅商常驻" }, + { 13, "随机天气" }, + { 14, "天气更新间隔分钟" }, + { 15, "夜间NPC入住" }, + { 16, "事件强制PVP" }, + { 17, "事件禁止切换队伍" } + }; - private void Team(object? o, GetDataHandlers.PlayerTeamEventArgs args)//队伍判断 + private void CmdRte(CommandArgs args) { - if (Main.bloodMoon == true || Main.eclipse == true || Main.pumpkinMoon == true || Main.snowMoon == true) + var player = args.Player; + var parameters = args.Parameters; + + if (parameters.Count == 0) { - args.Player.SetTeam(0); - args.Handled = true; - args.Player.SendInfoMessage(GetString("事件禁止切换队伍。")); + ShowConfig(player); + return; + } + + if (parameters.Count == 1) + { + player.SendErrorMessage("用法: /rte [序列号] [值]"); + player.SendInfoMessage("示例: /rte 1 true"); + return; + } + + string keyOrIndex = parameters[0]; + string value = parameters[1]; + + string? configKey = null; + + if (int.TryParse(keyOrIndex, out int index)) + { + if (ConfigIndexMap.TryGetValue(index, out configKey)) + { + } + else + { + player.SendErrorMessage($"[RealTime] 无效的序列号: {index},有效范围: 1-{ConfigIndexMap.Count}"); + return; + } + } + else + { + if (ConfigIndexMap.ContainsValue(keyOrIndex)) + { + configKey = keyOrIndex; + } + else + { + player.SendErrorMessage($"[RealTime] 未知的配置项: {keyOrIndex},请使用序列号 (1-{ConfigIndexMap.Count})"); + return; + } + } + + bool success = TrySetConfig(configKey!, value, out string feedback); + if (success) + { + Config.Save(ConfigPath); + player.SendSuccessMessage($"[RealTime] {feedback}"); + } + else + { + player.SendErrorMessage($"[RealTime] {feedback}"); } } - private void NPCS(On.Terraria.Main.orig_UpdateTime orig)//通过拦截时间更新,然后让其在非白天情况下npc入住 + private void ShowConfig(TSPlayer player) { - orig(); - if (!Main.dayTime) + player.SendInfoMessage("[c/FFD700:=== RealTime 当前配置 ===]"); + player.SendInfoMessage($"[c/90EE90:用法:] /rte [序列号] [值]"); + player.SendInfoMessage("[c/87CEEB:----------------------]"); + + foreach (var kvp in ConfigIndexMap.OrderBy(x => x.Key)) { - Main.UpdateTime_SpawnTownNPCs(true); + string displayValue = GetConfigDisplayValue(kvp.Value); + player.SendInfoMessage($"[c/FFFF00:({kvp.Key})] [c/87CEEB:{kvp.Value}:] {displayValue}"); } } - public ChangeCloud GetRandom(ChangeCloud[] arr)//自定义随机数定义 + private string GetConfigDisplayValue(string key) { - var ran = new Random(); - var n = ran.Next(arr.Length); - return arr[n]; + return key switch + { + "同步现实时间" => Bool(Config.EnableRealTimeSync), + "同步间隔秒" => Config.RealTimeSyncInterval.ToString(), + "事件自动结束" => Bool(Config.EnableEventAutoEnd), + "血月日食持续时间分钟" => Config.BloodMoonEclipseDuration.ToString(), + "霜月万圣节持续时间分钟" => Config.PumpkinMoonSnowMoonDuration.ToString(), + "渔夫任务刷新" => Bool(Config.EnableAnglerQuest), + "月相刷新" => Bool(Config.EnableMoonPhase), + "NPC综合刷新间隔分钟" => Config.NpcRefreshInterval.ToString(), + "老人自动召唤" => Bool(Config.EnableOldManSpawn), + "拜月教徒自动召唤" => Bool(Config.EnableCultistSpawn), + "旅商商品刷新" => Bool(Config.EnableTravelShopRefresh), + "旅商常驻" => Bool(Config.EnableTravelNPCStay), + "随机天气" => Bool(Config.EnableWeatherChange), + "天气更新间隔分钟" => Config.WeatherChangeInterval.ToString(), + "夜间NPC入住" => Bool(Config.EnableNightNpcSpawn), + "事件强制PVP" => Bool(Config.EnableEventForcePvp), + "事件禁止切换队伍" => Bool(Config.EnableEventTeamLock), + _ => "未知" + }; } - int i = 0; - int y = 0; - int q = 0; - bool lastBloodMoon = false; - bool lastEclipse = false; - bool lastPumpkinMoon = false; - bool lastSnowMoon = false; - DateTime time = DateTime.Now; - private void OnGameUpdate(EventArgs args) + private static string Bool(bool b) => b ? "[c/00FF00:开启]" : "[c/FF0000:关闭]"; + + private bool TrySetConfig(string key, string value, out string feedback) { - #region 血月 - if (this.lastBloodMoon ^ Main.bloodMoon) + try { - if (Main.bloodMoon) + switch (key) { - //血月开启瞬间 - this.time = DateTime.Now + TimeSpan.FromMinutes(20); + case "同步现实时间": + Config.EnableRealTimeSync = ParseBool(value); + feedback = $"同步现实时间已{BoolText(Config.EnableRealTimeSync)}"; + return true; + case "同步间隔秒": + Config.RealTimeSyncInterval = ParseInt(value, 1, 3600); + feedback = $"同步间隔秒已设置为 {Config.RealTimeSyncInterval}"; + return true; + case "事件自动结束": + Config.EnableEventAutoEnd = ParseBool(value); + feedback = $"事件自动结束已{BoolText(Config.EnableEventAutoEnd)}"; + return true; + case "血月日食持续时间分钟": + Config.BloodMoonEclipseDuration = ParseInt(value, 1, 120); + feedback = $"血月日食持续时间已设置为 {Config.BloodMoonEclipseDuration} 分钟"; + return true; + case "霜月万圣节持续时间分钟": + Config.PumpkinMoonSnowMoonDuration = ParseInt(value, 1, 120); + feedback = $"霜月万圣节持续时间已设置为 {Config.PumpkinMoonSnowMoonDuration} 分钟"; + return true; + case "渔夫任务刷新": + Config.EnableAnglerQuest = ParseBool(value); + feedback = $"渔夫任务刷新已{BoolText(Config.EnableAnglerQuest)}"; + return true; + case "月相刷新": + Config.EnableMoonPhase = ParseBool(value); + feedback = $"月相刷新已{BoolText(Config.EnableMoonPhase)}"; + return true; + case "NPC综合刷新间隔分钟": + Config.NpcRefreshInterval = ParseInt(value, 1, 1440); + feedback = $"NPC综合刷新间隔已设置为 {Config.NpcRefreshInterval} 分钟"; + return true; + case "老人自动召唤": + Config.EnableOldManSpawn = ParseBool(value); + feedback = $"老人自动召唤已{BoolText(Config.EnableOldManSpawn)}"; + return true; + case "拜月教徒自动召唤": + Config.EnableCultistSpawn = ParseBool(value); + feedback = $"拜月教徒自动召唤已{BoolText(Config.EnableCultistSpawn)}"; + return true; + case "旅商商品刷新": + Config.EnableTravelShopRefresh = ParseBool(value); + feedback = $"旅商商品刷新已{BoolText(Config.EnableTravelShopRefresh)}"; + return true; + case "旅商常驻": + Config.EnableTravelNPCStay = ParseBool(value); + feedback = $"旅商常驻已{BoolText(Config.EnableTravelNPCStay)}"; + return true; + case "随机天气": + Config.EnableWeatherChange = ParseBool(value); + feedback = $"随机天气已{BoolText(Config.EnableWeatherChange)}"; + return true; + case "天气更新间隔分钟": + Config.WeatherChangeInterval = ParseInt(value, 1, 1440); + feedback = $"天气更新间隔已设置为 {Config.WeatherChangeInterval} 分钟"; + return true; + case "夜间NPC入住": + Config.EnableNightNpcSpawn = ParseBool(value); + feedback = $"夜间NPC入住已{BoolText(Config.EnableNightNpcSpawn)}"; + return true; + case "事件强制PVP": + Config.EnableEventForcePvp = ParseBool(value); + feedback = $"事件强制PVP已{BoolText(Config.EnableEventForcePvp)}"; + return true; + case "事件禁止切换队伍": + Config.EnableEventTeamLock = ParseBool(value); + feedback = $"事件禁止切换队伍已{BoolText(Config.EnableEventTeamLock)}"; + return true; + default: + feedback = "未知配置项,请检查拼写"; + return false; } } - this.lastBloodMoon = Main.bloodMoon; - if (Main.bloodMoon == true) + catch (FormatException) { - TShock.Players.Where(p => p != null).ToList().ForEach(p => - { - if (p.TPlayer.hostile == false) - { - p.SetTeam(0); - p.TPlayer.hostile = true; - p.SendData(PacketTypes.TogglePvp, "", p.Index); - p.SendInfoMessage(GetString("血月的邪恶影响会阻止你的PvP关闭。")); - } - }); - if (DateTime.Now >= this.time) - { - Main.bloodMoon = false; - } + feedback = "值格式错误,布尔值请输入 true/false,数值请输入整数"; + return false; } - #endregion - #region 日食 - if (this.lastEclipse ^ Main.eclipse) + catch (ArgumentOutOfRangeException ex) { - if (Main.eclipse) - { - //日食开启瞬间 - this.time = DateTime.Now + TimeSpan.FromMinutes(20); - } + feedback = ex.Message; + return false; } - this.lastEclipse = Main.eclipse; - if (Main.eclipse == true) + } + + private static bool ParseBool(string value) + { + return value.ToLower() switch { - TShock.Players.Where(p => p != null).ToList().ForEach(p => - { - if (p.TPlayer.hostile == false) - { - p.TPlayer.hostile = true; - p.SetTeam(0); - p.SendData(PacketTypes.TogglePvp, "", p.Index); - p.SendInfoMessage(GetString("日食的邪恶影响会阻止你的PvP关闭。")); - } - }); - if (DateTime.Now >= this.time) - { - Main.eclipse = false; - } + "true" or "1" or "开" or "是" or "yes" or "on" => true, + "false" or "0" or "关" or "否" or "no" or "off" => false, + _ => throw new FormatException() + }; + } + + private static int ParseInt(string value, int min, int max) + { + int result = int.Parse(value); + if (result < min || result > max) + throw new ArgumentOutOfRangeException($"数值必须在 {min} ~ {max} 之间"); + return result; + } + + private static string BoolText(bool b) => b ? "开启" : "关闭"; + #endregion + + #region 重载配置 + private void OnReload(ReloadEventArgs args) + { + Config = RealTimeConfig.Load(ConfigPath); + args.Player?.SendSuccessMessage("[RealTime] 配置已重载。"); + } + #endregion + + #region 禁止旅商离开 + private void BlockUnspawn(On.Terraria.WorldGen.orig_UnspawnTravelNPC orig) + { + if (!Config.EnableTravelNPCStay) + { + orig(); + return; } - #endregion - #region 万圣节 - if (this.lastPumpkinMoon ^ Main.pumpkinMoon) + return; + } + #endregion + + #region 禁止事件期间切换队伍 + private void Team(object? o, GetDataHandlers.PlayerTeamEventArgs args) + { + if (!Config.EnableEventTeamLock) return; + if (Main.bloodMoon || Main.eclipse || Main.pumpkinMoon || Main.snowMoon) { - if (Main.pumpkinMoon) + args.Player.SetTeam(0); + args.Handled = true; + args.Player.SendInfoMessage("事件禁止切换队伍。"); + } + } + #endregion + + #region NPC夜间入住 + private void NPCS(On.Terraria.Main.orig_UpdateTime orig) + { + orig(); + if (!Config.EnableNightNpcSpawn) return; + if (!Main.dayTime) + { + Main.UpdateTime_SpawnTownNPCs(true); + } + } + #endregion + + #region 事件PvP逻辑 + private void EnforceEventPvP(string warningMessage) + { + if (!Config.EnableEventForcePvp) return; + foreach (var p in TShock.Players) + { + if (p != null && p.Active && !p.TPlayer.hostile) { - //万圣节开启瞬间 - this.time = DateTime.Now + TimeSpan.FromMinutes(40); + p.SetTeam(0); + p.TPlayer.hostile = true; + p.SendData(PacketTypes.TogglePvp, "", p.Index); + p.SendInfoMessage(warningMessage); } } - this.lastPumpkinMoon = Main.pumpkinMoon; - if (Main.pumpkinMoon == true) + } + #endregion + + #region 游戏更新逻辑 + private void OnGameUpdate(EventArgs args) + { + this.secondTimer++; + if (this.secondTimer >= 60) { - if (DateTime.Now >= this.time) + this.secondTimer = 0; + + if (Config.EnableEventAutoEnd) { - TShock.Players.Where(p => p != null).ToList().ForEach(p => + if (this.lastBloodMoon ^ Main.bloodMoon) + { + if (Main.bloodMoon) this.bloodMoonSecondsLeft = Config.BloodMoonEclipseDuration * 60; + } + this.lastBloodMoon = Main.bloodMoon; + if (Main.bloodMoon) + { + EnforceEventPvP("血月的邪恶影响会阻止你的PvP关闭。"); + this.bloodMoonSecondsLeft--; + if (this.bloodMoonSecondsLeft <= 0) + { + Main.bloodMoon = false; + NetMessage.SendData(7); + } + } + + if (this.lastEclipse ^ Main.eclipse) + { + if (Main.eclipse) this.eclipseSecondsLeft = Config.BloodMoonEclipseDuration * 60; + } + this.lastEclipse = Main.eclipse; + if (Main.eclipse) + { + EnforceEventPvP("日食的邪恶影响会阻止你的PvP关闭。"); + this.eclipseSecondsLeft--; + if (this.eclipseSecondsLeft <= 0) + { + Main.eclipse = false; + NetMessage.SendData(7); + } + } + + if (this.lastPumpkinMoon ^ Main.pumpkinMoon) { - if (p.TPlayer.hostile == false) + if (Main.pumpkinMoon) this.pumpkinMoonSecondsLeft = Config.PumpkinMoonSnowMoonDuration * 60; + } + this.lastPumpkinMoon = Main.pumpkinMoon; + if (Main.pumpkinMoon) + { + EnforceEventPvP("万圣节的邪恶影响会阻止你的PvP关闭。"); + this.pumpkinMoonSecondsLeft--; + if (this.pumpkinMoonSecondsLeft <= 0) + { + Main.pumpkinMoon = false; + NetMessage.SendData(7); + } + } + + if (this.lastSnowMoon ^ Main.snowMoon) + { + if (Main.snowMoon) this.snowMoonSecondsLeft = Config.PumpkinMoonSnowMoonDuration * 60; + } + this.lastSnowMoon = Main.snowMoon; + if (Main.snowMoon) + { + EnforceEventPvP("霜月的邪恶影响会阻止你的PvP关闭。"); + this.snowMoonSecondsLeft--; + if (this.snowMoonSecondsLeft <= 0) { - p.TPlayer.hostile = true; - p.SetTeam(0); - p.SendData(PacketTypes.TogglePvp, "", p.Index); - p.SendInfoMessage(GetString("万圣节的邪恶影响会阻止你的PvP关闭。")); + Main.snowMoon = false; + NetMessage.SendData(7); } - }); - Main.pumpkinMoon = false; + } + } + else + { + this.lastBloodMoon = Main.bloodMoon; + this.lastEclipse = Main.eclipse; + this.lastPumpkinMoon = Main.pumpkinMoon; + this.lastSnowMoon = Main.snowMoon; + + if (Main.bloodMoon) EnforceEventPvP("血月的邪恶影响会阻止你的PvP关闭。"); + if (Main.eclipse) EnforceEventPvP("日食的邪恶影响会阻止你的PvP关闭。"); + if (Main.pumpkinMoon) EnforceEventPvP("万圣节的邪恶影响会阻止你的PvP关闭。"); + if (Main.snowMoon) EnforceEventPvP("霜月的邪恶影响会阻止你的PvP关闭。"); } } - #endregion - #region 霜月 - if (this.lastSnowMoon ^ Main.snowMoon) + + if (Config.EnableRealTimeSync) { - if (Main.snowMoon) + this.realTimeSyncTimer++; + if (this.realTimeSyncTimer >= Config.RealTimeSyncInterval * 60) { - //霜月开启瞬间 - this.time = DateTime.Now + TimeSpan.FromMinutes(40); + var dt = DateTime.Now; + double d = dt.Hour + (dt.Minute / 60.0); + d -= 4.5; + if (d < 0.0) d += 24.0; + + if (d >= 15.0) + TSPlayer.Server.SetTime(false, (d - 15.0) * 3600.0); + else + TSPlayer.Server.SetTime(true, d * 3600.0); + + this.realTimeSyncTimer = 0; } } - this.lastSnowMoon = Main.snowMoon; - if (Main.snowMoon == true) + + this.npcWeatherTimer++; + if (this.npcWeatherTimer >= Config.NpcRefreshInterval * 60 * 60) { - if (DateTime.Now >= this.time) + if (Config.EnableOldManSpawn || Config.EnableCultistSpawn) { - TShock.Players.Where(p => p != null).ToList().ForEach(p => + bool oldManAlive = false; + bool cultistAlive = false; + for (int j = 0; j < Main.maxNPCs; j++) { - if (p.TPlayer.hostile == false) + var npc = Main.npc[j]; + if (npc != null && npc.active) { - p.TPlayer.hostile = true; - p.SetTeam(0); - p.SendData(PacketTypes.TogglePvp, "", p.Index); - p.SendInfoMessage(GetString("霜月的邪恶影响会阻止你的PvP关闭。")); + if (npc.netID == 37) oldManAlive = true; + if (npc.netID == 439) cultistAlive = true; } - }); - Main.snowMoon = false; + } + if (Config.EnableOldManSpawn && !oldManAlive && !NPC.downedBoss3) + TSPlayer.Server.SpawnNPC(37, "老人", 1, Main.dungeonX, Main.dungeonY, 50, 20); + if (Config.EnableCultistSpawn && !cultistAlive && !NPC.downedMoonlord && NPC.downedGolemBoss) + TSPlayer.Server.SpawnNPC(439, "教徒", 1, Main.dungeonX, Main.dungeonY, 50, 20); } - } - #endregion - #region 真实时间 - this.i++; - if (this.i == 480)//真实时间 - { - var dt = DateTime.Now; - dt.ToShortTimeString().ToString(); - var d = int.Parse(dt.Hour.ToString()) + (int.Parse(dt.Minute.ToString()) / 60.0m); - d -= 4.50m; - if (d < 0.00m) + + if (Config.EnableTravelShopRefresh) { - d += 24.00m; + Chest.SetupTravelShop(); + NetMessage.SendData(72); } - if (d >= 15.00m) + + if (Config.EnableAnglerQuest) { - TSPlayer.Server.SetTime(false, (double) ((d - 15.00m) * 3600.0m)); + Main.AnglerQuestSwap(); } - else + + if (Config.EnableAnglerQuest || Config.EnableTravelShopRefresh) { - TSPlayer.Server.SetTime(true, (double) (d * 3600.0m)); + TSPlayer.All.SendInfoMessage("渔夫任务和旅商商品已更换"); } - this.i = 0; + + if (Config.EnableMoonPhase) + { + int currentHour = DateTime.Now.Hour; + if (currentHour >= 19 || currentHour <= 4) + { + int nextMoon = (Main.moonPhase + 1) % 8; + Main.moonPhase = nextMoon; + NetMessage.SendData(7); + TSPlayer.All.SendInfoMessage($"月相已更换为:{this.GetMoon(nextMoon)}"); + } + } + this.npcWeatherTimer = 0; } - #endregion - #region npc生成、月相、天气、渔夫任务刷新 - this.y++; this.q++; - if (this.y == 86400)//npc生成 月相、天气、渔夫任务刷新 + + if (Config.EnableWeatherChange) { - var AllNPCS = Main.npc.Where(n => n != null); - foreach (var TNPC in AllNPCS) + this.weatherChangeTimer++; + if (this.weatherChangeTimer >= Config.WeatherChangeInterval * 60 * 60) { - if (TNPC.netID == 37) + Main.StopRain(); + Terraria.GameContent.Events.Sandstorm.StopSandstorm(); + Main.StopSlimeRain(); + Main.windSpeedTarget = 0f; + Main.windSpeedCurrent = 0f; + int r = rand.Next(100); + string weatherName; + if (r < 35) { - if (!TNPC.active)//遍历所有NPC,判断这个NPC是否存在 - { - if (!NPC.downedBoss3)//判断有没有击败骷髅王 - { - TSPlayer.Server.SpawnNPC(37, "老人", 200, Main.dungeonX, Main.dungeonY, 50, 20); - } - } + Main.numClouds = 0; + weatherName = "晴天"; } - if (TNPC.netID == 439) + else if (r < 60) { - if (!TNPC.active) - { - if (!NPC.downedMoonlord && NPC.downedGolemBoss)//没有击败月总但击败了石头人 - { - TSPlayer.Server.SpawnNPC(439, "教徒", 200, Main.dungeonX, Main.dungeonY, 50, 20); - } - } + Main.numClouds = 60; + Main.windSpeedTarget = 0.2f; + weatherName = "多云"; } - } - Chest.SetupTravelShop(); - NetMessage.SendData(72); - Main.AnglerQuestSwap();//更换渔夫任务 - TSPlayer.All.SendInfoMessage(GetString("渔夫任务和旅商商品已更换")); - if ((DateTime.Now.Hour >= 19 && DateTime.Now.Hour <= 24) || (0 <= DateTime.Now.Hour && DateTime.Now.Hour <= 4)) - { - if (0 <= Main.moonPhase + 1 && Main.moonPhase + 1 <= 7) + else if (r < 75) { - var msg = this.GetMoon(Main.moonPhase); - Main.moonPhase += 1; - NetMessage.SendData(7);//发月相包 - TSPlayer.All.SendInfoMessage(GetString($"月相已更换为:{msg}")); + Main.StartRain(); + Main.maxRaining = 0.3f; + Main.numClouds = rand.Next(60, 100); + weatherName = "小雨"; } - else + else if (r < 88) { - Main.moonPhase = 0; - NetMessage.SendData(7);//发月相包 - TSPlayer.All.SendInfoMessage(GetString("月相已更换为:满月")); + Main.StartRain(); + Main.maxRaining = 0.6f; + Main.numClouds = 70; + weatherName = "中雨"; } - } - else - { - var ran = new Random(); - var num = ran.Next(1, 50); //1-50随机数 - ChangeCloud[] arr = + else if (r < 96) + { + Main.StartRain(); + Main.maxRaining = 1.0f; + Main.windSpeedTarget = 0.8f; + Main.windSpeedCurrent = 0.8f; + Main.numClouds = rand.Next(80, 100); + weatherName = "暴雨/雷暴"; + } + else { - ()=>Main.cloudBGActive=num, - ()=>Main.cloudBGActive=0, - ()=>Main.numClouds=0, - ()=>Main.numClouds=10, - ()=>Main.numClouds=95, - ()=>Main.numClouds=60, - ()=>Main.StartRain(), //下雨 - ()=>Main.StopRain(), //雨停止 - ()=>Main.windSpeedCurrent=num,//风速 - ()=>Main.windSpeedTarget = num, - ()=>Main.maxRaining=0.3f, - ()=>Main.maxRaining=1, - ()=>Main.maxRaining=0.1f, - ()=>Terraria.GameContent.Events.Sandstorm.StartSandstorm(),//开始沙尘暴 - ()=>Terraria.GameContent.Events.Sandstorm.StopSandstorm() //停止沙尘暴 - }; - this.GetRandom(arr)(); + Terraria.GameContent.Events.Sandstorm.StartSandstorm(); + Main.windSpeedTarget = 0.7f; + Main.windSpeedCurrent = 0.7f; + weatherName = "沙尘暴"; + } + NetMessage.SendData(7); - TSPlayer.All.SendInfoMessage(GetString("天气已变更")); + TSPlayer.All.SendInfoMessage($"当前天气:{weatherName}"); + this.weatherChangeTimer = 0; } - this.y = 0; } - #endregion } + #endregion - private string GetMoon(int index) + #region 月相文本 + private string GetMoon(int phase) { - var arr = new[] + switch (phase) { - GetString("亏凸月"), - GetString("下弦月"), - GetString("残月"), - GetString("新月"), - GetString("娥眉月"), - GetString("上弦月"), - GetString("盈凸月") - }; - return index == -1 || index + 1 > arr.Length - ? GetString("[c/FF66FF:未知]") - : arr[index]; + case 0: return "满月"; + case 1: return "亏凸月"; + case 2: return "下弦月"; + case 3: return "残月"; + case 4: return "新月"; + case 5: return "娥眉月"; + case 6: return "上弦月"; + case 7: return "盈凸月"; + default: return "未知"; + } } + #endregion } \ No newline at end of file From 9c80ed0802826860788e77f2f6c61bb630958daf Mon Sep 17 00:00:00 2001 From: stardream <1011819146@qq.com> Date: Sat, 27 Jun 2026 18:45:04 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/RealTime/Config.cs | 78 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/RealTime/Config.cs diff --git a/src/RealTime/Config.cs b/src/RealTime/Config.cs new file mode 100644 index 000000000..8f00d1699 --- /dev/null +++ b/src/RealTime/Config.cs @@ -0,0 +1,78 @@ +using System; +using System.IO; +using Newtonsoft.Json; + +namespace RealTime; + +public class RealTimeConfig +{ + [JsonProperty("同步现实时间")] + public bool EnableRealTimeSync { get; set; } = true; + + [JsonProperty("同步间隔秒")] + public int RealTimeSyncInterval { get; set; } = 8; + + [JsonProperty("事件自动结束")] + public bool EnableEventAutoEnd { get; set; } = true; + + [JsonProperty("血月日食持续时间分钟")] + public int BloodMoonEclipseDuration { get; set; } = 20; + + [JsonProperty("霜月万圣节持续时间分钟")] + public int PumpkinMoonSnowMoonDuration { get; set; } = 40; + + [JsonProperty("渔夫任务刷新")] + public bool EnableAnglerQuest { get; set; } = true; + + [JsonProperty("月相刷新")] + public bool EnableMoonPhase { get; set; } = true; + + [JsonProperty("NPC综合刷新间隔分钟")] + public int NpcRefreshInterval { get; set; } = 24; + + [JsonProperty("老人自动召唤")] + public bool EnableOldManSpawn { get; set; } = true; + + [JsonProperty("拜月教徒自动召唤")] + public bool EnableCultistSpawn { get; set; } = true; + + [JsonProperty("旅商商品刷新")] + public bool EnableTravelShopRefresh { get; set; } = true; + + [JsonProperty("旅商常驻")] + public bool EnableTravelNPCStay { get; set; } = true; + + [JsonProperty("随机天气")] + public bool EnableWeatherChange { get; set; } = true; + + [JsonProperty("天气更新间隔分钟")] + public int WeatherChangeInterval { get; set; } = 10; + + [JsonProperty("夜间NPC入住")] + public bool EnableNightNpcSpawn { get; set; } = true; + + [JsonProperty("事件强制PVP")] + public bool EnableEventForcePvp { get; set; } = true; + + [JsonProperty("事件禁止切换队伍")] + public bool EnableEventTeamLock { get; set; } = true; + + public static RealTimeConfig Load(string path) + { + if (!File.Exists(path)) + { + var cfg = new RealTimeConfig(); + cfg.Save(path); + return cfg; + } + var json = File.ReadAllText(path); + var config = JsonConvert.DeserializeObject(json); + return config ?? new RealTimeConfig(); + } + + public void Save(string path) + { + var json = JsonConvert.SerializeObject(this, Formatting.Indented); + File.WriteAllText(path, json); + } +} \ No newline at end of file From 571d393a2a2570b4df1c8cfe310c890387f4d199 Mon Sep 17 00:00:00 2001 From: stardream <1011819146@qq.com> Date: Sat, 27 Jun 2026 18:45:12 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/RealTime/RealTime.csproj | 37 ++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/RealTime/RealTime.csproj b/src/RealTime/RealTime.csproj index 6973768bb..f23aed881 100644 --- a/src/RealTime/RealTime.csproj +++ b/src/RealTime/RealTime.csproj @@ -1,5 +1,38 @@ - + + net9.0 + 14 + enable + RealTime + RealTime + - + + + + + + + $(TShockPath)\OTAPI.dll + false + + + $(TShockPath)\OTAPI.Runtime.dll + false + + + $(TShockPath)\TerrariaServer.dll + false + + + $(TShockPath)\TShockAPI.dll + false + + + $(TShockPath)\TerrariaApi.Server.dll + false + + + + \ No newline at end of file From 69064064e7213e6330be14f00e6f24145e842505 Mon Sep 17 00:00:00 2001 From: stardream <1011819146@qq.com> Date: Sat, 27 Jun 2026 18:45:28 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/RealTime/README.md | 70 +++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/src/RealTime/README.md b/src/RealTime/README.md index 55dd867c7..50a2a80d0 100644 --- a/src/RealTime/README.md +++ b/src/RealTime/README.md @@ -1,48 +1,68 @@ -# RealTime 现实时间 - -- 作者: 十七 -- 出处: 作者原创 -- 版本:1.4.4.9 -- 介绍: -- 会同步本地时间,每8秒同步一次,所以服务器内可能会有短暂的和本地时间不匹配的情况 -- 并且血月和日食开启20分钟后自动结束,霜月、万圣节开启后40分钟自动结束 -- 渔夫任务和月相每24分钟刷新一次 -- 由于夜晚不生成npc,所以老人和拜月教邪教徒每24分钟检测一次,如果不存在就召唤一个,召唤位置在地牢。 -- 白天随机天气功能,24分钟更新一次 -- 夜间npc入住功能 -- 事件发生时,使其强制开启PVP模式!!! +# RealTime 同步真实时间 + +- 作者: 十七、星梦优化 +- 出处: `https://github.com/ICU-Club` +- 同步现实时间到游戏内时间,支持事件自动结束、NPC自动召唤、随机天气等功能 ## 指令 -``` -暂无 -``` +| 语法 | 权限 | 说明 | +|-----|:----:|------| +| /reload | tshock.cfg.reload | 重载插件配置 | +| /rte | realtime.admin | 查看当前所有配置 | +| /rte <序列号> <值> | realtime.admin | 修改指定配置项 | ## 配置 +> 配置文件路径:tshock/realtime.json -```json -暂无 +```json5 +{ + "同步现实时间": true, // 是否同步现实时间到游戏内 + "同步间隔秒": 8, // 同步间隔(秒) + "事件自动结束": true, // 是否自动结束事件 + "血月日食持续时间分钟": 20, // 血月/日食持续时间(分钟) + "霜月万圣节持续时间分钟": 40, // 霜月/万圣节持续时间(分钟) + "渔夫任务刷新": true, // 是否自动刷新渔夫任务 + "月相刷新": true, // 是否自动推进月相 + "NPC综合刷新间隔分钟": 24, // NPC刷新间隔(分钟) + "老人自动召唤": true, // 是否自动召唤老人 + "拜月教徒自动召唤": true, // 是否自动召唤拜月教徒 + "旅商商品刷新": true, // 是否刷新旅商商品 + "旅商常驻": true, // 是否阻止旅商离开 + "随机天气": true, // 是否启用随机天气 + "天气更新间隔分钟": 10, // 天气更新间隔(分钟) + "夜间NPC入住": true, // 是否允许夜间NPC入住 + "事件强制PVP": true, // 事件期间是否强制PVP + "事件禁止切换队伍": true // 事件期间是否禁止切换队伍 +} ``` ## 更新日志 +### v2.8.0 +- 新增 `/rte` 指令序列号支持,配置项前显示编号 +- 配置文件路径简化为 `tshock/realtime.json` +- 支持通过序列号快速修改配置 + +### v2.7 +- 新增旅商常驻功能 +- 优化天气、月相刷新代码 + ### v2.6 - 新增旅商24分钟更新一次售卖物品 ### v2.5 -- 修复强制pvp可以切换队伍的bug +- 修复强制PVP可以切换队伍的bug ### v2.4 - 更改事件发生时,使其强制开启PVP模式 ### v2.3 -- 新增白天随机天气功能,15分钟更新一次 +- 新增白天随机天气功能 ### v2.2 -- 新增夜间npc入住功能 - +- 新增夜间NPC入住功能 ## 反馈 -- 优先发issued -> 共同维护的插件库:https://github.com/UnrealMultiple/TShockPlugin -- 次优先:TShock官方群:816771079 -- 大概率看不到但是也可以:国内社区trhub.cn ,bbstr.net , tr.monika.love +优先发 issued -> 共同维护的插件库:`https://github.com/ICU-Club` +次优先:TShock官方群:816771079 \ No newline at end of file From 704679e82b247ef78eb27b6d5e9a6aa5ad0e362f Mon Sep 17 00:00:00 2001 From: stardream <1011819146@qq.com> Date: Sat, 27 Jun 2026 18:53:17 +0800 Subject: [PATCH 5/7] Update README.md --- src/RealTime/README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/RealTime/README.md b/src/RealTime/README.md index 50a2a80d0..ccd6dc6d0 100644 --- a/src/RealTime/README.md +++ b/src/RealTime/README.md @@ -1,7 +1,6 @@ # RealTime 同步真实时间 - 作者: 十七、星梦优化 -- 出处: `https://github.com/ICU-Club` - 同步现实时间到游戏内时间,支持事件自动结束、NPC自动召唤、随机天气等功能 ## 指令 @@ -40,9 +39,7 @@ ## 更新日志 ### v2.8.0 -- 新增 `/rte` 指令序列号支持,配置项前显示编号 -- 配置文件路径简化为 `tshock/realtime.json` -- 支持通过序列号快速修改配置 +- 新增 `/rte` 在服务器中快速修改配置 ### v2.7 - 新增旅商常驻功能 @@ -64,5 +61,5 @@ - 新增夜间NPC入住功能 ## 反馈 -优先发 issued -> 共同维护的插件库:`https://github.com/ICU-Club` -次优先:TShock官方群:816771079 \ No newline at end of file +优先发 issued -> 共同维护的插件库: +次优先:TShock官方群:816771079 From 1225d8c4c789c519c4555f12f6707711a1df20fe Mon Sep 17 00:00:00 2001 From: stardream <1011819146@qq.com> Date: Sat, 27 Jun 2026 18:53:49 +0800 Subject: [PATCH 6/7] Revise feedback section in README Updated feedback section with new contact methods. --- src/RealTime/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/RealTime/README.md b/src/RealTime/README.md index ccd6dc6d0..7fc786377 100644 --- a/src/RealTime/README.md +++ b/src/RealTime/README.md @@ -60,6 +60,6 @@ ### v2.2 - 新增夜间NPC入住功能 -## 反馈 -优先发 issued -> 共同维护的插件库: -次优先:TShock官方群:816771079 +- 优先发issued -> 共同维护的插件库:https://github.com/UnrealMultiple/TShockPlugin +- 次优先:TShock官方群:816771079 +- 大概率看不到但是也可以:国内社区trhub.cn ,bbstr.net , tr.monika.love From bfe0f937d910ae47861dbb309d8537d52bc39bc7 Mon Sep 17 00:00:00 2001 From: stardream <1011819146@qq.com> Date: Sat, 27 Jun 2026 18:59:48 +0800 Subject: [PATCH 7/7] Delete src/RealTime/RealTime.csproj --- src/RealTime/RealTime.csproj | 38 ------------------------------------ 1 file changed, 38 deletions(-) delete mode 100644 src/RealTime/RealTime.csproj diff --git a/src/RealTime/RealTime.csproj b/src/RealTime/RealTime.csproj deleted file mode 100644 index f23aed881..000000000 --- a/src/RealTime/RealTime.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - net9.0 - 14 - enable - RealTime - RealTime - - - - - - - - - $(TShockPath)\OTAPI.dll - false - - - $(TShockPath)\OTAPI.Runtime.dll - false - - - $(TShockPath)\TerrariaServer.dll - false - - - $(TShockPath)\TShockAPI.dll - false - - - $(TShockPath)\TerrariaApi.Server.dll - false - - - - \ No newline at end of file