Add RealTime plugin v2.8.0#1169
Conversation
There was a problem hiding this comment.
你好,我发现了 3 个问题,并且有一些整体性的反馈:
- 实时同步计时器现在是每秒自增一次(在
secondTimer >= 60代码块中),但仍然和RealTimeSyncInterval * 60比较,这会导致按“秒”配置的间隔在实际效果上变成“分钟”。建议直接将realTimeSyncTimer与RealTimeSyncInterval比较,而不是RealTimeSyncInterval * 60。 - 在
ConfigIndexMap、GetConfigDisplayValue和TrySetConfig中有大量重复使用的配置键;建议将这些键集中管理(例如使用枚举,或者一个同时包含 getter/setter 委托的统一映射结构),以减少在新增或重命名配置项时出现不匹配的风险。 OnGameUpdate方法承担了很多彼此无关的职责(事件计时器、PvP 限制、实时同步、NPC 生成、渔夫/旅商刷新、月相、天气),逻辑比较难以阅读。建议将其拆分为多个私有方法(例如UpdateEvents、UpdateRealTime、UpdateNpcAndShops、UpdateWeather),以提升可读性和可维护性。
供 AI 代理使用的提示语
Please address the comments from this code review:
## Overall Comments
- The real-time sync timer now increments once per second (inside the `secondTimer >= 60` block) but still compares against `RealTimeSyncInterval * 60`, so a configured interval in seconds effectively becomes minutes; consider comparing `realTimeSyncTimer` directly to `RealTimeSyncInterval` instead of `RealTimeSyncInterval * 60`.
- There is a lot of duplicated configuration key usage across `ConfigIndexMap`, `GetConfigDisplayValue`, and `TrySetConfig`; centralizing these keys (e.g., with an enum or a single mapping structure that includes getter/setter delegates) would reduce the risk of mismatches when adding or renaming options.
- The `OnGameUpdate` method is doing many unrelated responsibilities (event timers, PvP enforcement, real time sync, NPC spawn, angler/travel shop refresh, moon phase, weather), which makes it hard to follow; consider splitting this into separate private methods (e.g., `UpdateEvents`, `UpdateRealTime`, `UpdateNpcAndShops`, `UpdateWeather`) to improve readability and maintainability.
## Individual Comments
### Comment 1
<location path="src/RealTime/RealTime.cs" line_range="115-124" />
<code_context>
+
+ string? configKey = null;
+
+ if (int.TryParse(keyOrIndex, out int index))
+ {
+ if (ConfigIndexMap.TryGetValue(index, out configKey))
+ {
+ }
</code_context>
<issue_to_address>
**suggestion:** 避免在基于索引的配置查找中出现空的 if 代码块,以提高可读性
在 `CmdRte` 中,`ConfigIndexMap.TryGetValue(index, out configKey)` 的成功分支是空的,所有逻辑都写在 `else` 里,这会让人误以为查找成功时什么都没做。建议反转条件,在失败时尽早返回:
```csharp
if (!ConfigIndexMap.TryGetValue(index, out configKey))
{
player.SendErrorMessage($"[RealTime] 无效的序列号: {index},有效范围: 1-{ConfigIndexMap.Count}");
return;
}
```
这样可以更清晰地表达“成功路径”的意图,并降低在后续为成功查找添加逻辑时出错的风险。
```suggestion
string keyOrIndex = parameters[0];
string value = parameters[1];
string? configKey = null;
if (int.TryParse(keyOrIndex, out int index))
{
if (!ConfigIndexMap.TryGetValue(index, out configKey))
{
player.SendErrorMessage($"[RealTime] 无效的序列号: {index},有效范围: 1-{ConfigIndexMap.Count}");
return;
}
}
else
{
configKey = keyOrIndex;
}
```
</issue_to_address>
### Comment 2
<location path="src/RealTime/RealTime.cs" line_range="505-515" />
<code_context>
}
- else
+
+ if (Config.EnableAnglerQuest || Config.EnableTravelShopRefresh)
{
- TSPlayer.Server.SetTime(true, (double) (d * 3600.0m));
+ TSPlayer.All.SendInfoMessage("渔夫任务和旅商商品已更换");
}
- this.i = 0;
</code_context>
<issue_to_address>
**suggestion:** 让广播消息与实际执行的刷新操作保持一致
现在只要 `EnableAnglerQuest` 或 `EnableTravelShopRefresh` 任意一个为 true,就会发送“渔夫任务和旅商商品已更换”这条消息,即使实际上只刷新了其中一个,这可能导致不准确的公告。建议根据实际执行的操作调整消息内容(例如使用单独的消息,或者动态拼接字符串),这样玩家看到的更新就能精确反映已启用并执行刷新的功能。
```suggestion
if (Config.EnableAnglerQuest)
{
Main.AnglerQuestSwap();
}
string refreshMessage = null;
if (Config.EnableAnglerQuest && Config.EnableTravelShopRefresh)
{
refreshMessage = "渔夫任务和旅商商品已更换";
}
else if (Config.EnableAnglerQuest)
{
refreshMessage = "渔夫任务已更换";
}
else if (Config.EnableTravelShopRefresh)
{
refreshMessage = "旅商商品已更换";
}
if (refreshMessage != null)
{
TSPlayer.All.SendInfoMessage(refreshMessage);
}
if (Config.EnableMoonPhase)
```
</issue_to_address>
### Comment 3
<location path="src/RealTime/README.md" line_range="67" />
<code_context>
-- 优先发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
</code_context>
<issue_to_address>
**issue (typo):** 建议将 "issued" 改为 "issue",以符合常见 GitHub 术语
在 GitHub 语境中,“优先发 issue” 是更常见的说法,使用 “issue” 会更自然一些。
</issue_to_address>请帮我变得更有用!欢迎在每条评论上点 👍 或 👎,我会根据反馈改进之后的评审。
Original comment in English
Hey - I've found 3 issues, and left some high level feedback:
- The real-time sync timer now increments once per second (inside the
secondTimer >= 60block) but still compares againstRealTimeSyncInterval * 60, so a configured interval in seconds effectively becomes minutes; consider comparingrealTimeSyncTimerdirectly toRealTimeSyncIntervalinstead ofRealTimeSyncInterval * 60. - There is a lot of duplicated configuration key usage across
ConfigIndexMap,GetConfigDisplayValue, andTrySetConfig; centralizing these keys (e.g., with an enum or a single mapping structure that includes getter/setter delegates) would reduce the risk of mismatches when adding or renaming options. - The
OnGameUpdatemethod is doing many unrelated responsibilities (event timers, PvP enforcement, real time sync, NPC spawn, angler/travel shop refresh, moon phase, weather), which makes it hard to follow; consider splitting this into separate private methods (e.g.,UpdateEvents,UpdateRealTime,UpdateNpcAndShops,UpdateWeather) to improve readability and maintainability.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The real-time sync timer now increments once per second (inside the `secondTimer >= 60` block) but still compares against `RealTimeSyncInterval * 60`, so a configured interval in seconds effectively becomes minutes; consider comparing `realTimeSyncTimer` directly to `RealTimeSyncInterval` instead of `RealTimeSyncInterval * 60`.
- There is a lot of duplicated configuration key usage across `ConfigIndexMap`, `GetConfigDisplayValue`, and `TrySetConfig`; centralizing these keys (e.g., with an enum or a single mapping structure that includes getter/setter delegates) would reduce the risk of mismatches when adding or renaming options.
- The `OnGameUpdate` method is doing many unrelated responsibilities (event timers, PvP enforcement, real time sync, NPC spawn, angler/travel shop refresh, moon phase, weather), which makes it hard to follow; consider splitting this into separate private methods (e.g., `UpdateEvents`, `UpdateRealTime`, `UpdateNpcAndShops`, `UpdateWeather`) to improve readability and maintainability.
## Individual Comments
### Comment 1
<location path="src/RealTime/RealTime.cs" line_range="115-124" />
<code_context>
+
+ string? configKey = null;
+
+ if (int.TryParse(keyOrIndex, out int index))
+ {
+ if (ConfigIndexMap.TryGetValue(index, out configKey))
+ {
+ }
</code_context>
<issue_to_address>
**suggestion:** Avoid empty if-block in index-based config lookup for clarity
In `CmdRte`, `ConfigIndexMap.TryGetValue(index, out configKey)` has an empty success branch with all logic in the `else`, which makes it look like the success case does nothing. Consider inverting the condition and returning early on failure:
```csharp
if (!ConfigIndexMap.TryGetValue(index, out configKey))
{
player.SendErrorMessage($"[RealTime] 无效的序列号: {index},有效范围: 1-{ConfigIndexMap.Count}");
return;
}
```
This makes the intent of the success path clearer and reduces the risk of errors if additional logic is later added for the successful lookup.
```suggestion
string keyOrIndex = parameters[0];
string value = parameters[1];
string? configKey = null;
if (int.TryParse(keyOrIndex, out int index))
{
if (!ConfigIndexMap.TryGetValue(index, out configKey))
{
player.SendErrorMessage($"[RealTime] 无效的序列号: {index},有效范围: 1-{ConfigIndexMap.Count}");
return;
}
}
else
{
configKey = keyOrIndex;
}
```
</issue_to_address>
### Comment 2
<location path="src/RealTime/RealTime.cs" line_range="505-515" />
<code_context>
}
- else
+
+ if (Config.EnableAnglerQuest || Config.EnableTravelShopRefresh)
{
- TSPlayer.Server.SetTime(true, (double) (d * 3600.0m));
+ TSPlayer.All.SendInfoMessage("渔夫任务和旅商商品已更换");
}
- this.i = 0;
</code_context>
<issue_to_address>
**suggestion:** Align broadcast message with which refresh actions actually ran
The message "渔夫任务和旅商商品已更换" is sent whenever either `EnableAnglerQuest` or `EnableTravelShopRefresh` is true, even if only one of them actually runs. This can result in inaccurate announcements. Consider tailoring the message to the specific operations performed (e.g., separate messages or a dynamically constructed string) so players only see updates for features that are enabled and refreshed.
```suggestion
if (Config.EnableAnglerQuest)
{
Main.AnglerQuestSwap();
}
string refreshMessage = null;
if (Config.EnableAnglerQuest && Config.EnableTravelShopRefresh)
{
refreshMessage = "渔夫任务和旅商商品已更换";
}
else if (Config.EnableAnglerQuest)
{
refreshMessage = "渔夫任务已更换";
}
else if (Config.EnableTravelShopRefresh)
{
refreshMessage = "旅商商品已更换";
}
if (refreshMessage != null)
{
TSPlayer.All.SendInfoMessage(refreshMessage);
}
if (Config.EnableMoonPhase)
```
</issue_to_address>
### Comment 3
<location path="src/RealTime/README.md" line_range="67" />
<code_context>
-- 优先发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
</code_context>
<issue_to_address>
**issue (typo):** Consider changing "issued" to "issue" to match common GitHub wording.
In GitHub context, “优先发 issue” is the standard phrasing, so using “issue” here will read more naturally.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| string keyOrIndex = parameters[0]; | ||
| string value = parameters[1]; | ||
|
|
||
| string? configKey = null; | ||
|
|
||
| if (int.TryParse(keyOrIndex, out int index)) | ||
| { | ||
| if (ConfigIndexMap.TryGetValue(index, out configKey)) | ||
| { | ||
| } |
There was a problem hiding this comment.
suggestion: 避免在基于索引的配置查找中出现空的 if 代码块,以提高可读性
在 CmdRte 中,ConfigIndexMap.TryGetValue(index, out configKey) 的成功分支是空的,所有逻辑都写在 else 里,这会让人误以为查找成功时什么都没做。建议反转条件,在失败时尽早返回:
if (!ConfigIndexMap.TryGetValue(index, out configKey))
{
player.SendErrorMessage($"[RealTime] 无效的序列号: {index},有效范围: 1-{ConfigIndexMap.Count}");
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)) | |
| { | |
| } | |
| string keyOrIndex = parameters[0]; | |
| string value = parameters[1]; | |
| string? configKey = null; | |
| if (int.TryParse(keyOrIndex, out int index)) | |
| { | |
| if (!ConfigIndexMap.TryGetValue(index, out configKey)) | |
| { | |
| player.SendErrorMessage($"[RealTime] 无效的序列号: {index},有效范围: 1-{ConfigIndexMap.Count}"); | |
| return; | |
| } | |
| } | |
| else | |
| { | |
| configKey = keyOrIndex; | |
| } |
Original comment in English
suggestion: Avoid empty if-block in index-based config lookup for clarity
In CmdRte, ConfigIndexMap.TryGetValue(index, out configKey) has an empty success branch with all logic in the else, which makes it look like the success case does nothing. Consider inverting the condition and returning early on failure:
if (!ConfigIndexMap.TryGetValue(index, out configKey))
{
player.SendErrorMessage($"[RealTime] 无效的序列号: {index},有效范围: 1-{ConfigIndexMap.Count}");
return;
}This makes the intent of the success path clearer and reduces the risk of errors if additional logic is later added for the successful lookup.
| string keyOrIndex = parameters[0]; | |
| string value = parameters[1]; | |
| string? configKey = null; | |
| if (int.TryParse(keyOrIndex, out int index)) | |
| { | |
| if (ConfigIndexMap.TryGetValue(index, out configKey)) | |
| { | |
| } | |
| string keyOrIndex = parameters[0]; | |
| string value = parameters[1]; | |
| string? configKey = null; | |
| if (int.TryParse(keyOrIndex, out int index)) | |
| { | |
| if (!ConfigIndexMap.TryGetValue(index, out configKey)) | |
| { | |
| player.SendErrorMessage($"[RealTime] 无效的序列号: {index},有效范围: 1-{ConfigIndexMap.Count}"); | |
| return; | |
| } | |
| } | |
| else | |
| { | |
| configKey = keyOrIndex; | |
| } |
| 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) |
There was a problem hiding this comment.
suggestion: 让广播消息与实际执行的刷新操作保持一致
现在只要 EnableAnglerQuest 或 EnableTravelShopRefresh 任意一个为 true,就会发送“渔夫任务和旅商商品已更换”这条消息,即使实际上只刷新了其中一个,这可能导致不准确的公告。建议根据实际执行的操作调整消息内容(例如使用单独的消息,或者动态拼接字符串),这样玩家看到的更新就能精确反映已启用并执行刷新的功能。
| 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) | |
| if (Config.EnableAnglerQuest) | |
| { | |
| Main.AnglerQuestSwap(); | |
| } | |
| string refreshMessage = null; | |
| if (Config.EnableAnglerQuest && Config.EnableTravelShopRefresh) | |
| { | |
| refreshMessage = "渔夫任务和旅商商品已更换"; | |
| } | |
| else if (Config.EnableAnglerQuest) | |
| { | |
| refreshMessage = "渔夫任务已更换"; | |
| } | |
| else if (Config.EnableTravelShopRefresh) | |
| { | |
| refreshMessage = "旅商商品已更换"; | |
| } | |
| if (refreshMessage != null) | |
| { | |
| TSPlayer.All.SendInfoMessage(refreshMessage); | |
| } | |
| if (Config.EnableMoonPhase) |
Original comment in English
suggestion: Align broadcast message with which refresh actions actually ran
The message "渔夫任务和旅商商品已更换" is sent whenever either EnableAnglerQuest or EnableTravelShopRefresh is true, even if only one of them actually runs. This can result in inaccurate announcements. Consider tailoring the message to the specific operations performed (e.g., separate messages or a dynamically constructed string) so players only see updates for features that are enabled and refreshed.
| 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) | |
| if (Config.EnableAnglerQuest) | |
| { | |
| Main.AnglerQuestSwap(); | |
| } | |
| string refreshMessage = null; | |
| if (Config.EnableAnglerQuest && Config.EnableTravelShopRefresh) | |
| { | |
| refreshMessage = "渔夫任务和旅商商品已更换"; | |
| } | |
| else if (Config.EnableAnglerQuest) | |
| { | |
| refreshMessage = "渔夫任务已更换"; | |
| } | |
| else if (Config.EnableTravelShopRefresh) | |
| { | |
| refreshMessage = "旅商商品已更换"; | |
| } | |
| if (refreshMessage != null) | |
| { | |
| TSPlayer.All.SendInfoMessage(refreshMessage); | |
| } | |
| if (Config.EnableMoonPhase) |
| - 优先发issued -> 共同维护的插件库:https://github.com/UnrealMultiple/TShockPlugin | ||
| - 次优先:TShock官方群:816771079 | ||
| - 大概率看不到但是也可以:国内社区trhub.cn ,bbstr.net , tr.monika.love | ||
| 优先发 issued -> 共同维护的插件库:`https://github.com/ICU-Club` |
There was a problem hiding this comment.
issue (typo): 建议将 "issued" 改为 "issue",以符合常见 GitHub 术语
在 GitHub 语境中,“优先发 issue” 是更常见的说法,使用 “issue” 会更自然一些。
Original comment in English
issue (typo): Consider changing "issued" to "issue" to match common GitHub wording.
In GitHub context, “优先发 issue” is the standard phrasing, so using “issue” here will read more naturally.
| # RealTime 同步真实时间 | ||
|
|
||
| - 作者: 十七、星梦优化 | ||
| - 出处: `https://github.com/ICU-Club` |
| } | ||
|
|
||
| if (parameters.Count == 1) | ||
| { |
| #region 指令系统 | ||
| private static readonly Dictionary<int, string> ConfigIndexMap = new() | ||
| { | ||
| { 1, "同步现实时间" }, |
| if (int.TryParse(keyOrIndex, out int index)) | ||
| { | ||
| if (ConfigIndexMap.TryGetValue(index, out configKey)) | ||
| { |
There was a problem hiding this comment.
这里直接!ConfigIndexMap.TryGetValue(index, out configKey)就好了,留空代码块有什么作用吗 (
| } | ||
|
|
||
| bool success = TrySetConfig(configKey!, value, out string feedback); | ||
| if (success) |
| { | ||
| switch (key) | ||
| { | ||
| case "同步现实时间": |
Updated feedback section with new contact methods.
|
啥,不是十七原创吗
原始邮件
发件人:Cai ***@***.***>
发件时间:2026年6月27日 18:55
收件人:UnrealMultiple/TShockPlugin ***@***.***>
抄送:stardream ***@***.***>, Author ***@***.***>
主题:Re: [UnrealMultiple/TShockPlugin] Add RealTime plugin v2.8.0 (PR #1169)
@ACaiCat commented on this pull request.
In src/RealTime/README.md:
> -- 作者: 十七 -- 出处: 作者原创 -- 版本:1.4.5.6 -- 介绍: -- 会同步本地时间,每8秒同步一次,所以服务器内可能会有短暂的和本地时间不匹配的情况 -- 并且血月和日食开启20分钟后自动结束,霜月、万圣节开启后40分钟自动结束 -- 渔夫任务和月相每24分钟刷新一次 -- 由于夜晚不生成npc,所以老人和拜月教邪教徒每24分钟检测一次,如果不存在就召唤一个,召唤位置在地牢。 -- 白天随机天气功能,10分钟更新一次 -- 夜间npc入住功能 -- 事件发生时,使其强制开启PVP模式!!! -- 旅商常驻功能,24分钟更新一次售卖物品 +# RealTime 同步真实时间 + +- 作者: 十七、星梦优化 +- 出处: `https://github.com/ICU-Club`
出处是这个插件原来是哪里来的
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you authored the thread.
|
|
这个问十七吧,我也不知道
原始邮件
发件人:Cai ***@***.***>
发件时间:2026年6月27日 18:55
收件人:UnrealMultiple/TShockPlugin ***@***.***>
抄送:stardream ***@***.***>, Author ***@***.***>
主题:Re: [UnrealMultiple/TShockPlugin] Add RealTime plugin v2.8.0 (PR #1169)
@ACaiCat commented on this pull request.
In src/RealTime/README.md:
> -- 作者: 十七 -- 出处: 作者原创 -- 版本:1.4.5.6 -- 介绍: -- 会同步本地时间,每8秒同步一次,所以服务器内可能会有短暂的和本地时间不匹配的情况 -- 并且血月和日食开启20分钟后自动结束,霜月、万圣节开启后40分钟自动结束 -- 渔夫任务和月相每24分钟刷新一次 -- 由于夜晚不生成npc,所以老人和拜月教邪教徒每24分钟检测一次,如果不存在就召唤一个,召唤位置在地牢。 -- 白天随机天气功能,10分钟更新一次 -- 夜间npc入住功能 -- 事件发生时,使其强制开启PVP模式!!! -- 旅商常驻功能,24分钟更新一次售卖物品 +# RealTime 同步真实时间 + +- 作者: 十七、星梦优化 +- 出处: `https://github.com/ICU-Club`
出处是这个插件原来是哪里来的
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you authored the thread.
|
RealTime 插件 v2.8.0
更新内容
/rte指令现在支持通过序列号快速修改配置