Skip to content

Add RealTime plugin v2.8.0#1169

Open
star620 wants to merge 7 commits into
UnrealMultiple:masterfrom
star620:main
Open

Add RealTime plugin v2.8.0#1169
star620 wants to merge 7 commits into
UnrealMultiple:masterfrom
star620:main

Conversation

@star620

@star620 star620 commented Jun 27, 2026

Copy link
Copy Markdown
Member

RealTime 插件 v2.8.0

更新内容

  • 序列号配置支持/rte 指令现在支持通过序列号快速修改配置

@star620 star620 requested a review from a team as a code owner June 27, 2026 10:45

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你好,我发现了 3 个问题,并且有一些整体性的反馈:

  • 实时同步计时器现在是每秒自增一次(在 secondTimer >= 60 代码块中),但仍然和 RealTimeSyncInterval * 60 比较,这会导致按“秒”配置的间隔在实际效果上变成“分钟”。建议直接将 realTimeSyncTimerRealTimeSyncInterval 比较,而不是 RealTimeSyncInterval * 60
  • ConfigIndexMapGetConfigDisplayValueTrySetConfig 中有大量重复使用的配置键;建议将这些键集中管理(例如使用枚举,或者一个同时包含 getter/setter 委托的统一映射结构),以减少在新增或重命名配置项时出现不匹配的风险。
  • OnGameUpdate 方法承担了很多彼此无关的职责(事件计时器、PvP 限制、实时同步、NPC 生成、渔夫/旅商刷新、月相、天气),逻辑比较难以阅读。建议将其拆分为多个私有方法(例如 UpdateEventsUpdateRealTimeUpdateNpcAndShopsUpdateWeather),以提升可读性和可维护性。
供 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>

Sourcery 对开源项目免费——如果你觉得这次评审有帮助,欢迎分享 ✨
请帮我变得更有用!欢迎在每条评论上点 👍 或 👎,我会根据反馈改进之后的评审。
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 >= 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.
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>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/RealTime/RealTime.cs
Comment on lines +115 to +124
string keyOrIndex = parameters[0];
string value = parameters[1];

string? configKey = null;

if (int.TryParse(keyOrIndex, out int index))
{
if (ConfigIndexMap.TryGetValue(index, out configKey))
{
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: 避免在基于索引的配置查找中出现空的 if 代码块,以提高可读性

CmdRte 中,ConfigIndexMap.TryGetValue(index, out configKey) 的成功分支是空的,所有逻辑都写在 else 里,这会让人误以为查找成功时什么都没做。建议反转条件,在失败时尽早返回:

if (!ConfigIndexMap.TryGetValue(index, out configKey))
{
    player.SendErrorMessage($"[RealTime] 无效的序列号: {index},有效范围: 1-{ConfigIndexMap.Count}");
    return;
}

这样可以更清晰地表达“成功路径”的意图,并降低在后续为成功查找添加逻辑时出错的风险。

Suggested change
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.

Suggested change
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;
}

Comment thread src/RealTime/RealTime.cs
Comment on lines +505 to +515
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: 让广播消息与实际执行的刷新操作保持一致

现在只要 EnableAnglerQuestEnableTravelShopRefresh 任意一个为 true,就会发送“渔夫任务和旅商商品已更换”这条消息,即使实际上只刷新了其中一个,这可能导致不准确的公告。建议根据实际执行的操作调整消息内容(例如使用单独的消息,或者动态拼接字符串),这样玩家看到的更新就能精确反映已启用并执行刷新的功能。

Suggested change
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.

Suggested change
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)

Comment thread src/RealTime/README.md Outdated
- 优先发issued -> 共同维护的插件库:https://github.com/UnrealMultiple/TShockPlugin
- 次优先:TShock官方群:816771079
- 大概率看不到但是也可以:国内社区trhub.cn ,bbstr.net , tr.monika.love
优先发 issued -> 共同维护的插件库:`https://github.com/ICU-Club`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/RealTime/README.md Outdated
# RealTime 同步真实时间

- 作者: 十七、星梦优化
- 出处: `https://github.com/ICU-Club`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hyw 你改这个的时候问过17了吗 (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

出处是这个插件原来是哪里来的

Comment thread src/RealTime/RealTime.csproj Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

改回去

Comment thread src/RealTime/RealTime.cs
}

if (parameters.Count == 1)
{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetString !!!!!

Comment thread src/RealTime/RealTime.cs
#region 指令系统
private static readonly Dictionary<int, string> ConfigIndexMap = new()
{
{ 1, "同步现实时间" },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一样,字符串都要GetString

Comment thread src/RealTime/RealTime.cs
if (int.TryParse(keyOrIndex, out int index))
{
if (ConfigIndexMap.TryGetValue(index, out configKey))
{

@ACaiCat ACaiCat Jun 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里直接!ConfigIndexMap.TryGetValue(index, out configKey)就好了,留空代码块有什么作用吗 (

Comment thread src/RealTime/RealTime.cs
}

bool success = TrySetConfig(configKey!, value, out string feedback);
if (success)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里直接塞进去吧,没啥必要放个中间变量

Comment thread src/RealTime/RealTime.cs
{
switch (key)
{
case "同步现实时间":

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里感觉可以直接用索引啊,case字符串不好做多语言

Updated feedback section with new contact methods.
@star620

star620 commented Jun 27, 2026 via email

Copy link
Copy Markdown
Member Author

@star620

star620 commented Jun 27, 2026 via email

Copy link
Copy Markdown
Member Author

@star620 star620 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(•_•)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants