Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 124 additions & 5 deletions src/MusicPlayer/MusicPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
public override string Description => GetString("一个简单的音乐播放插件.");

public override string Name => System.Reflection.Assembly.GetExecutingAssembly().GetName().Name!;
public override Version Version => new Version(1, 0, 8);
public override Version Version => new Version(1, 0, 9);

public string songPath = Path.Combine(TShock.SavePath, "Songs");

Expand All @@ -30,7 +30,8 @@
{
new ("song", this.PlaySong, "song"),
new ("songall", this.PlaySongAll, "songall"),
new ("songlist", this.ListFiles, "songlist")
new ("songlist", this.ListFiles, "songlist"),
new ("songqueue", this.SongQueueCommand, "songqueue")
};
}

Expand Down Expand Up @@ -68,7 +69,7 @@

private void OnLeave(LeaveEventArgs args)
{
SongPlayers[args.Who] = null; // 移除对应的 SongPlayer 对象
SongPlayers[args.Who] = null;
ListeningCheck();
}
public static void ListeningCheck()
Expand Down Expand Up @@ -170,6 +171,7 @@
args.Player.SendInfoMessage(GetString("方式: /song <歌曲名称/索引号> [演奏乐器]\n演奏乐器: harp, theaxe, bell,默认为 harp"));
args.Player.SendInfoMessage(GetString("使用 /song 来停止播放."));
args.Player.SendInfoMessage(GetString("使用 /songlist 查看歌曲索引."));
args.Player.SendInfoMessage(GetString("使用 /songqueue 管理点歌队列."));
}
}
else
Expand All @@ -185,7 +187,6 @@
var notes = NoteFileParser.Read(filePath!, out var tempo);
isNoOneListening = false;
var performer = VirtualPerformer.GetPerformer(args.Parameters.ElementAtOrDefault(1));
performer.Create(songPlayer.Player.Index);
songPlayer.StartSong(new PlaySongInfo(notes, tempo, performer));
args.Player.SendInfoMessage(GetString("正在播放: {0}"), songName);
}
Expand All @@ -211,7 +212,6 @@
if (songPlayer is not null)
{
var performer = VirtualPerformer.GetPerformer(args.Parameters.ElementAtOrDefault(1));
performer.Create(i);
songPlayer.StartSong(new PlaySongInfo(notes, tempo, performer));
if (TShock.Players[i].Active)
{
Expand All @@ -234,6 +234,125 @@
}
}

private void SongQueueCommand(CommandArgs args)
{
var songPlayer = SongPlayers[args.Player.Index];
if (songPlayer is null) return;

if (args.Parameters.Count == 0 || args.Parameters[0].ToLower() == "list")
Comment on lines +237 to +242
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: 当没有 SongPlayer 时,建议向用户提供反馈,而不是静默返回。

当玩家在其 SongPlayer 尚未创建之前运行 /songqueue 时,命令会直接结束且没有任何输出,这对用户来说会比较困惑。建议发送一个简短的信息/错误提示,说明点歌系统尚未初始化,或者提示他们需要先开始播放一首歌。

Suggested change
private void SongQueueCommand(CommandArgs args)
{
var songPlayer = SongPlayers[args.Player.Index];
if (songPlayer is null) return;
if (args.Parameters.Count == 0 || args.Parameters[0].ToLower() == "list")
private void SongQueueCommand(CommandArgs args)
{
var songPlayer = SongPlayers[args.Player.Index];
if (songPlayer is null)
{
args.Player.SendErrorMessage(GetString("歌曲系统尚未初始化,请先播放一首歌后再使用此命令。"));
return;
}
if (args.Parameters.Count == 0 || args.Parameters[0].ToLower() == "list")
Original comment in English

suggestion: Consider providing user feedback when there is no SongPlayer instead of returning silently.

When a player runs /songqueue before their SongPlayer exists, the command exits with no output, which is confusing for users. Consider sending a short info/error message indicating that the song system isn’t initialized yet or that they need to start playing a song first.

Suggested change
private void SongQueueCommand(CommandArgs args)
{
var songPlayer = SongPlayers[args.Player.Index];
if (songPlayer is null) return;
if (args.Parameters.Count == 0 || args.Parameters[0].ToLower() == "list")
private void SongQueueCommand(CommandArgs args)
{
var songPlayer = SongPlayers[args.Player.Index];
if (songPlayer is null)
{
args.Player.SendErrorMessage(GetString("歌曲系统尚未初始化,请先播放一首歌后再使用此命令。"));
return;
}
if (args.Parameters.Count == 0 || args.Parameters[0].ToLower() == "list")

{
if (songPlayer.SongQueue.Count == 0)

Check failure on line 244 in src/MusicPlayer/MusicPlayer.cs

View workflow job for this annotation

GitHub Actions / 构建插件

'SongPlayer' does not contain a definition for 'SongQueue' and no accessible extension method 'SongQueue' accepting a first argument of type 'SongPlayer' could be found (are you missing a using directive or an assembly reference?)
{
args.Player.SendInfoMessage(GetString("当前点歌队列为空"));
return;
}
var sb = new StringBuilder();
sb.AppendLine(GetString("=== 点歌队列 ({0}首) ===", songPlayer.SongQueue.Count));

Check failure on line 250 in src/MusicPlayer/MusicPlayer.cs

View workflow job for this annotation

GitHub Actions / 构建插件

'SongPlayer' does not contain a definition for 'SongQueue' and no accessible extension method 'SongQueue' accepting a first argument of type 'SongPlayer' could be found (are you missing a using directive or an assembly reference?)
int i = 1;
foreach (var song in songPlayer.SongQueue)

Check failure on line 252 in src/MusicPlayer/MusicPlayer.cs

View workflow job for this annotation

GitHub Actions / 构建插件

'SongPlayer' does not contain a definition for 'SongQueue' and no accessible extension method 'SongQueue' accepting a first argument of type 'SongPlayer' could be found (are you missing a using directive or an assembly reference?)
{
sb.AppendLine(GetString("{0}. {1}音符", i, song.Notes.Count));
i++;
}
args.Player.SendMessage(sb.ToString(), Microsoft.Xna.Framework.Color.Yellow);
return;
}

if (args.Parameters[0].ToLower() == "clear")
{
songPlayer.SongQueue.Clear();

Check failure on line 263 in src/MusicPlayer/MusicPlayer.cs

View workflow job for this annotation

GitHub Actions / 构建插件

'SongPlayer' does not contain a definition for 'SongQueue' and no accessible extension method 'SongQueue' accepting a first argument of type 'SongPlayer' could be found (are you missing a using directive or an assembly reference?)
args.Player.SendSuccessMessage(GetString("已清空点歌队列"));
return;
}

if (args.Parameters[0].ToLower() == "play")
{
if (songPlayer.Listening)
{
args.Player.SendInfoMessage(GetString("已经在播放中..."));
return;
}
if (songPlayer.SongQueue.Count == 0)

Check failure on line 275 in src/MusicPlayer/MusicPlayer.cs

View workflow job for this annotation

GitHub Actions / 构建插件

'SongPlayer' does not contain a definition for 'SongQueue' and no accessible extension method 'SongQueue' accepting a first argument of type 'SongPlayer' could be found (are you missing a using directive or an assembly reference?)
{
args.Player.SendErrorMessage(GetString("队列为空,无法播放"));
Comment on lines +268 to +277
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: 可以通过先统一规范子命令字符串,然后使用 switch 或查找表来简化子命令处理逻辑。

当前方法在为每个子命令做判断时,会多次重复调用 args.Parameters[0].ToLower()。可以考虑先将其赋值给一个变量(例如 var sub = args.Parameters[0].ToLowerInvariant();),然后通过 switch 或查找表分发逻辑。这样可以减少重复,并让后续新增子命令时更简单、更不易出错。

Original comment in English

suggestion: The subcommand handling could be simplified by normalizing the subcommand once and using a switch or lookup.

This method repeats args.Parameters[0].ToLower() across multiple if checks for each subcommand. Consider assigning it once (e.g. var sub = args.Parameters[0].ToLowerInvariant();) and using a switch or lookup to dispatch. This will cut duplication and keep future subcommand additions simpler and less error-prone.

return;
}
songPlayer.PlayNext();

Check failure on line 280 in src/MusicPlayer/MusicPlayer.cs

View workflow job for this annotation

GitHub Actions / 构建插件

'SongPlayer' does not contain a definition for 'PlayNext' and no accessible extension method 'PlayNext' accepting a first argument of type 'SongPlayer' could be found (are you missing a using directive or an assembly reference?)
return;
}

if (args.Parameters[0].ToLower() == "skip")
{
if (!songPlayer.Listening)
{
args.Player.SendErrorMessage(GetString("当前没有正在播放的歌曲"));
return;
}
songPlayer.EndSong(false);

Check failure on line 291 in src/MusicPlayer/MusicPlayer.cs

View workflow job for this annotation

GitHub Actions / 构建插件

No overload for method 'EndSong' takes 1 arguments
args.Player.SendSuccessMessage(GetString("已跳过当前歌曲"));
return;
}

if (args.Parameters[0].ToLower() == "global")
{
if (args.Parameters.Count < 2)
{
args.Player.SendInfoMessage(GetString("用法: /songqueue global <歌曲名/索引> [instrument]"));
return;
}
var search = args.Parameters[1];
var instrument = args.Parameters.ElementAtOrDefault(2);

if (!TryResolveSong(search, out var filePath, out var songName, out var errorMessage))
{
args.Player.SendErrorMessage(errorMessage);
return;
}

var notes = NoteFileParser.Read(filePath!, out var tempo);
var performer = VirtualPerformer.GetPerformer(instrument);

int count = 0;
for (int i = 0; i < SongPlayers.Length; i++)
{
var sp = SongPlayers[i];
if (sp != null)
{
var songInfo = new PlaySongInfo(notes, tempo, performer);
sp.EnqueueSong(songInfo);

Check failure on line 322 in src/MusicPlayer/MusicPlayer.cs

View workflow job for this annotation

GitHub Actions / 构建插件

'SongPlayer' does not contain a definition for 'EnqueueSong' and no accessible extension method 'EnqueueSong' accepting a first argument of type 'SongPlayer' could be found (are you missing a using directive or an assembly reference?)
count++;
if (TShock.Players[i]?.Active == true)
{
TShock.Players[i].SendInfoMessage(GetString("全局点歌: {0} 已加入您的队列", songName));
}
}
}
args.Player.SendSuccessMessage(GetString("已为 {0} 位在线玩家添加歌曲到队列", count));
return;
}

var searchTerm = args.Parameters[0];
var inst = args.Parameters.ElementAtOrDefault(1);

if (!TryResolveSong(searchTerm, out var fp, out var sn, out var em))
{
args.Player.SendErrorMessage(em);
return;
}

var nts = NoteFileParser.Read(fp!, out var tmp);
var perf = VirtualPerformer.GetPerformer(inst);

var newSong = new PlaySongInfo(nts, tmp, perf);
songPlayer.EnqueueSong(newSong);

Check failure on line 347 in src/MusicPlayer/MusicPlayer.cs

View workflow job for this annotation

GitHub Actions / 构建插件

'SongPlayer' does not contain a definition for 'EnqueueSong' and no accessible extension method 'EnqueueSong' accepting a first argument of type 'SongPlayer' could be found (are you missing a using directive or an assembly reference?)
args.Player.SendSuccessMessage(GetString("已将 {0} 加入点歌队列 (当前队列: {1}首)", sn, songPlayer.SongQueue.Count));

Check failure on line 348 in src/MusicPlayer/MusicPlayer.cs

View workflow job for this annotation

GitHub Actions / 构建插件

'SongPlayer' does not contain a definition for 'SongQueue' and no accessible extension method 'SongQueue' accepting a first argument of type 'SongPlayer' could be found (are you missing a using directive or an assembly reference?)
Comment on lines +334 to +348
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: 在这个命令处理器中,过短且缺乏语义的变量名会降低可读性。

在这一段代码中,像 fpsnemntstmpperf 这样的名字会让逻辑更难理解。请使用更清晰的命名(例如 filePathsongNameerrorMessagenotestempoperformer),以提高这个本就比较复杂的处理函数的可读性。

Suggested change
var searchTerm = args.Parameters[0];
var inst = args.Parameters.ElementAtOrDefault(1);
if (!TryResolveSong(searchTerm, out var fp, out var sn, out var em))
{
args.Player.SendErrorMessage(em);
return;
}
var nts = NoteFileParser.Read(fp!, out var tmp);
var perf = VirtualPerformer.GetPerformer(inst);
var newSong = new PlaySongInfo(nts, tmp, perf);
songPlayer.EnqueueSong(newSong);
args.Player.SendSuccessMessage(GetString("已将 {0} 加入点歌队列 (当前队列: {1}首)", sn, songPlayer.SongQueue.Count));
var searchTerm = args.Parameters[0];
var inst = args.Parameters.ElementAtOrDefault(1);
if (!TryResolveSong(searchTerm, out var filePath, out var songName, out var errorMessage))
{
args.Player.SendErrorMessage(errorMessage);
return;
}
var notes = NoteFileParser.Read(filePath!, out var tempo);
var performer = VirtualPerformer.GetPerformer(inst);
var newSong = new PlaySongInfo(notes, tempo, performer);
songPlayer.EnqueueSong(newSong);
args.Player.SendSuccessMessage(GetString("已将 {0} 加入点歌队列 (当前队列: {1}首)", songName, songPlayer.SongQueue.Count));
Original comment in English

suggestion: Short, non-descriptive variable names reduce readability in this command handler.

In this block, names like fp, sn, em, nts, tmp, and perf make the logic harder to follow. Please use clearer names (e.g., filePath, songName, errorMessage, notes, tempo, performer) to improve readability in an already complex handler.

Suggested change
var searchTerm = args.Parameters[0];
var inst = args.Parameters.ElementAtOrDefault(1);
if (!TryResolveSong(searchTerm, out var fp, out var sn, out var em))
{
args.Player.SendErrorMessage(em);
return;
}
var nts = NoteFileParser.Read(fp!, out var tmp);
var perf = VirtualPerformer.GetPerformer(inst);
var newSong = new PlaySongInfo(nts, tmp, perf);
songPlayer.EnqueueSong(newSong);
args.Player.SendSuccessMessage(GetString("已将 {0} 加入点歌队列 (当前队列: {1}首)", sn, songPlayer.SongQueue.Count));
var searchTerm = args.Parameters[0];
var inst = args.Parameters.ElementAtOrDefault(1);
if (!TryResolveSong(searchTerm, out var filePath, out var songName, out var errorMessage))
{
args.Player.SendErrorMessage(errorMessage);
return;
}
var notes = NoteFileParser.Read(filePath!, out var tempo);
var performer = VirtualPerformer.GetPerformer(inst);
var newSong = new PlaySongInfo(notes, tempo, performer);
songPlayer.EnqueueSong(newSong);
args.Player.SendSuccessMessage(GetString("已将 {0} 加入点歌队列 (当前队列: {1}首)", songName, songPlayer.SongQueue.Count));


if (!songPlayer.Listening)
{
args.Player.SendInfoMessage(GetString("使用 /songqueue play 开始播放队列"));
}
}

private void ListFiles(CommandArgs args)
{
var targetFolder = Path.Combine(TShock.SavePath, "Songs");
Expand Down
Loading