public async Task ExecuteGroupAsync(CommandContext ctx, [Description("desc-game-movetime")] TimeSpan?moveTime = null) { if (moveTime?.TotalSeconds is < 2 or > 120) { throw new InvalidCommandUsageException(ctx, "cmd-err-game-movetime", 2, 120); } if (this.Service.IsEventRunningInChannel(ctx.Channel.Id)) { throw new CommandFailedException(ctx, "cmd-err-evt-dup"); } DiscordUser?opponent = await ctx.WaitForGameOpponentAsync(); if (opponent is null) { throw new CommandFailedException(ctx, "cmd-err-game-op-none", ctx.User.Mention); } var game = new Connect4Game(ctx.Client.GetInteractivity(), ctx.Channel, ctx.User, opponent, moveTime); this.Service.RegisterEventInChannel(game, ctx.Channel.Id); try { await game.RunAsync(this.Localization); if (game.Winner is { }) { if (game.IsTimeoutReached) { await ctx.ImpInfoAsync(this.ModuleColor, Emojis.Trophy, "str-game-timeout", game.Winner.Mention); } else { await ctx.ImpInfoAsync(this.ModuleColor, Emojis.Trophy, "fmt-winners", game.Winner.Mention); } GameStatsService gss = ctx.Services.GetRequiredService <GameStatsService>(); await gss.UpdateStatsAsync(game.Winner.Id, s => s.Connect4Won++); await gss.UpdateStatsAsync(game.Winner == ctx.User?opponent.Id : ctx.User.Id, s => s.Connect4Lost++); }