change: Removed a lot of old useless/broken commands. Improved .listserver. Full changes in changelog

This commit is contained in:
Kwoth
2024-08-05 15:56:32 +00:00
parent 764babdf06
commit 4a402ee673
17 changed files with 105 additions and 848 deletions

View File

@@ -38,11 +38,11 @@ public sealed class Connect4Game : IDisposable
public Phase CurrentPhase { get; private set; } = Phase.Joining;
public ImmutableArray<Field> GameState
=> _gameState.ToImmutableArray();
public IReadOnlyList<Field> GameState
=> _gameState.AsReadOnly();
public ImmutableArray<(ulong UserId, string Username)?> Players
=> _players.ToImmutableArray();
public IReadOnlyCollection<(ulong UserId, string Username)?> Players
=> _players.AsReadOnly();
public (ulong UserId, string Username) CurrentPlayer
=> CurrentPhase == Phase.P1Move ? _players[0].Value : _players[1].Value;
@@ -56,7 +56,6 @@ public sealed class Connect4Game : IDisposable
private readonly SemaphoreSlim _locker = new(1, 1);
private readonly Options _options;
private readonly ICurrencyService _cs;
private readonly NadekoRandom _rng;
private Timer playerTimeoutTimer;
@@ -73,12 +72,11 @@ public sealed class Connect4Game : IDisposable
public Connect4Game(
ulong userId,
string userName,
Options options,
ICurrencyService cs)
Options options
)
{
_players[0] = (userId, userName);
_options = options;
_cs = cs;
_rng = new();
for (var i = 0; i < NUMBER_OF_COLUMNS * NUMBER_OF_ROWS; i++)
@@ -99,14 +97,13 @@ public sealed class Connect4Game : IDisposable
{
_ = OnGameFailedToStart?.Invoke(this);
CurrentPhase = Phase.Ended;
await _cs.AddAsync(_players[0].Value.UserId, _options.Bet, new("connect4", "refund"));
}
}
finally { _locker.Release(); }
});
}
public async Task<bool> Join(ulong userId, string userName, int bet)
public async Task<bool> Join(ulong userId, string userName)
{
await _locker.WaitAsync();
try
@@ -117,11 +114,6 @@ public sealed class Connect4Game : IDisposable
if (_players[0].Value.UserId == userId) // same user can't join own game
return false;
if (bet != _options.Bet) // can't join if bet amount is not the same
return false;
if (!await _cs.RemoveAsync(userId, bet, new("connect4", "bet"))) // user doesn't have enough money to gamble
return false;
if (_rng.Next(0, 2) == 0) //rolling from 0-1, if number is 0, join as first player
{
@@ -351,13 +343,8 @@ public sealed class Connect4Game : IDisposable
if (result == Result.Draw)
{
_cs.AddAsync(CurrentPlayer.UserId, _options.Bet, new("connect4", "draw"));
_cs.AddAsync(OtherPlayer.UserId, _options.Bet, new("connect4", "draw"));
return;
}
if (winId is not null)
_cs.AddAsync(winId.Value, (long)(_options.Bet * 1.98), new("connect4", "win"));
}
private Field GetPlayerPiece(ulong userId)
@@ -394,16 +381,10 @@ public sealed class Connect4Game : IDisposable
HelpText = "Turn time in seconds. It has to be between 5 and 60. Default 15.")]
public int TurnTimer { get; set; } = 15;
[Option('b', "bet", Required = false, Default = 0, HelpText = "Amount you bet. Default 0.")]
public int Bet { get; set; }
public void NormalizeOptions()
{
if (TurnTimer is < 5 or > 60)
TurnTimer = 15;
if (Bet < 0)
Bet = 0;
}
}
}

View File

@@ -29,17 +29,15 @@ public partial class Gambling
}
private readonly DiscordSocketClient _client;
private readonly ICurrencyService _cs;
private IUserMessage msg;
private int repostCounter;
public Connect4Commands(DiscordSocketClient client, ICurrencyService cs, GamblingConfigService gamb)
public Connect4Commands(DiscordSocketClient client, GamblingConfigService gamb)
: base(gamb)
{
_client = client;
_cs = cs;
}
[Cmd]
@@ -48,10 +46,8 @@ public partial class Gambling
public async Task Connect4(params string[] args)
{
var (options, _) = OptionsParser.ParseFrom(new Connect4Game.Options(), args);
if (!await CheckBetOptional(options.Bet))
return;
var newGame = new Connect4Game(ctx.User.Id, ctx.User.ToString(), options, _cs);
var newGame = new Connect4Game(ctx.User.Id, ctx.User.ToString(), options);
Connect4Game game;
if ((game = _service.Connect4Games.GetOrAdd(ctx.Channel.Id, newGame)) != newGame)
{
@@ -60,31 +56,17 @@ public partial class Gambling
newGame.Dispose();
//means game already exists, try to join
await game.Join(ctx.User.Id, ctx.User.ToString(), options.Bet);
await game.Join(ctx.User.Id, ctx.User.ToString());
return;
}
if (options.Bet > 0)
{
if (!await _cs.RemoveAsync(ctx.User.Id, options.Bet, new("connect4", "bet")))
{
await Response().Error(strs.not_enough(CurrencySign)).SendAsync();
_service.Connect4Games.TryRemove(ctx.Channel.Id, out _);
game.Dispose();
return;
}
}
game.OnGameStateUpdated += Game_OnGameStateUpdated;
game.OnGameFailedToStart += GameOnGameFailedToStart;
game.OnGameEnded += GameOnGameEnded;
_client.MessageReceived += ClientMessageReceived;
game.Initialize();
if (options.Bet == 0)
await Response().Confirm(strs.connect4_created).SendAsync();
else
await Response().Error(strs.connect4_created_bet(N(options.Bet))).SendAsync();
await Response().Confirm(strs.connect4_created).SendAsync();
Task ClientMessageReceived(SocketMessage arg)
{
@@ -151,19 +133,19 @@ public partial class Gambling
title = GetText(strs.connect4_draw);
return msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
.WithTitle(title)
.WithDescription(GetGameStateText(game))
.WithOkColor()
.Build());
.WithTitle(title)
.WithDescription(GetGameStateText(game))
.WithOkColor()
.Build());
}
}
private async Task Game_OnGameStateUpdated(Connect4Game game)
{
var embed = _sender.CreateEmbed()
.WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}")
.WithDescription(GetGameStateText(game))
.WithOkColor();
.WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}")
.WithDescription(GetGameStateText(game))
.WithOkColor();
if (msg is null)
@@ -198,7 +180,7 @@ public partial class Gambling
for (var i = 0; i < Connect4Game.NUMBER_OF_COLUMNS; i++)
sb.Append(_numbers[i]);
return sb.ToString();
}
}

View File

@@ -30,8 +30,6 @@ public partial class Gambling : GamblingModule<GamblingService>
private readonly GamblingTxTracker _gamblingTxTracker;
private readonly IPatronageService _ps;
private IUserMessage rdMsg;
public Gambling(
IGamblingService gs,
DbService db,
@@ -104,34 +102,6 @@ public partial class Gambling : GamblingModule<GamblingService>
await Response().Embed(eb).SendAsync();
}
[Cmd]
public async Task Economy()
{
var ec = await _service.GetEconomyAsync();
decimal onePercent = 0;
// This stops the top 1% from owning more than 100% of the money
if (ec.Cash > 0)
{
onePercent = ec.OnePercent / (ec.Cash - ec.Bot);
}
// [21:03] Bob Page: Kinda remids me of US economy
var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.economy_state))
.AddField(GetText(strs.currency_owned), N(ec.Cash - ec.Bot))
.AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
.AddField(GetText(strs.currency_planted), N(ec.Planted))
.AddField(GetText(strs.owned_waifus_total), N(ec.Waifus))
.AddField(GetText(strs.bot_currency), N(ec.Bot))
.AddField(GetText(strs.bank_accounts), N(ec.Bank))
.AddField(GetText(strs.total), N(ec.Cash + ec.Planted + ec.Waifus + ec.Bank))
.WithOkColor();
// ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table
await Response().Embed(embed).SendAsync();
}
private async Task RemindTimelyAction(SocketMessageComponent smc, DateTime when)
{
var tt = TimestampTag.FromDateTime(when, TimestampTagStyles.Relative);
@@ -601,117 +571,6 @@ public partial class Gambling : GamblingModule<GamblingService>
}
}
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task RollDuel(IUser u)
{
if (ctx.User.Id == u.Id)
{
return;
}
//since the challenge is created by another user, we need to reverse the ids
//if it gets removed, means challenge is accepted
if (_service.Duels.TryRemove((ctx.User.Id, u.Id), out var game))
{
await game.StartGame();
}
}
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task RollDuel([OverrideTypeReader(typeof(BalanceTypeReader))] long amount, IUser u)
{
if (ctx.User.Id == u.Id)
{
return;
}
if (amount <= 0)
{
return;
}
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.roll_duel));
var description = string.Empty;
var game = new RollDuelGame(_cs, _client.CurrentUser.Id, ctx.User.Id, u.Id, amount);
//means challenge is just created
if (_service.Duels.TryGetValue((ctx.User.Id, u.Id), out var other))
{
if (other.Amount != amount)
{
await Response().Error(strs.roll_duel_already_challenged).SendAsync();
}
else
{
await RollDuel(u);
}
return;
}
if (_service.Duels.TryAdd((u.Id, ctx.User.Id), game))
{
game.OnGameTick += GameOnGameTick;
game.OnEnded += GameOnEnded;
await Response()
.Confirm(strs.roll_duel_challenge(Format.Bold(ctx.User.ToString()),
Format.Bold(u.ToString()),
Format.Bold(N(amount))))
.SendAsync();
}
async Task GameOnGameTick(RollDuelGame arg)
{
var rolls = arg.Rolls.Last();
description += $@"{Format.Bold(ctx.User.ToString())} rolled **{rolls.Item1}**
{Format.Bold(u.ToString())} rolled **{rolls.Item2}**
--
";
embed = embed.WithDescription(description);
if (rdMsg is null)
{
rdMsg = await Response().Embed(embed).SendAsync();
}
else
{
await rdMsg.ModifyAsync(x => { x.Embed = embed.Build(); });
}
}
async Task GameOnEnded(RollDuelGame rdGame, RollDuelGame.Reason reason)
{
try
{
if (reason == RollDuelGame.Reason.Normal)
{
var winner = rdGame.Winner == rdGame.P1 ? ctx.User : u;
description += $"\n**{winner}** Won {N((long)(rdGame.Amount * 2 * 0.98))}";
embed = embed.WithDescription(description);
await rdMsg.ModifyAsync(x => x.Embed = embed.Build());
}
else if (reason == RollDuelGame.Reason.Timeout)
{
await Response().Error(strs.roll_duel_timeout).SendAsync();
}
else if (reason == RollDuelGame.Reason.NoFunds)
{
await Response().Error(strs.roll_duel_no_funds).SendAsync();
}
}
finally
{
_service.Duels.TryRemove((u.Id, ctx.User.Id), out _);
}
}
}
[Cmd]
public async Task BetRoll([OverrideTypeReader(typeof(BalanceTypeReader))] long amount)
{

View File

@@ -128,38 +128,6 @@ public class GamblingService : INService, IReadyExecutor
private static readonly TypedKey<EconomyResult> _ecoKey = new("nadeko:economy");
public async Task<EconomyResult> GetEconomyAsync()
{
var data = await _cache.GetOrAddAsync(_ecoKey,
async () =>
{
await using var uow = _db.GetDbContext();
var cash = uow.Set<DiscordUser>().GetTotalCurrency();
var onePercent = uow.Set<DiscordUser>().GetTopOnePercentCurrency(_client.CurrentUser.Id);
decimal planted = uow.Set<PlantedCurrency>().AsQueryable().Sum(x => x.Amount);
var waifus = uow.Set<WaifuInfo>().GetTotalValue();
var bot = await uow.Set<DiscordUser>().GetUserCurrencyAsync(_client.CurrentUser.Id);
decimal bank = await uow.GetTable<BankUser>()
.SumAsyncLinqToDB(x => x.Balance);
var result = new EconomyResult
{
Cash = cash,
Planted = planted,
Bot = bot,
Waifus = waifus,
OnePercent = onePercent,
Bank = bank
};
return result;
},
TimeSpan.FromMinutes(3));
return data;
}
private static readonly SemaphoreSlim _timelyLock = new(1, 1);
private static TypedKey<Dictionary<ulong, long>> _timelyKey

View File

@@ -1,61 +0,0 @@
#nullable disable
using NadekoBot.Common.TypeReaders;
using NadekoBot.Modules.Gambling.Common;
using NadekoBot.Modules.Gambling.Services;
namespace NadekoBot.Modules.Gambling;
public partial class Gambling
{
public partial class CurrencyRaffleCommands : GamblingSubmodule<CurrencyRaffleService>
{
public enum Mixed { Mixed }
public CurrencyRaffleCommands(GamblingConfigService gamblingConfService)
: base(gamblingConfService)
{
}
[Cmd]
[RequireContext(ContextType.Guild)]
[Priority(0)]
public Task RaffleCur(Mixed _, [OverrideTypeReader(typeof(BalanceTypeReader))] long amount)
=> RaffleCur(amount, true);
[Cmd]
[RequireContext(ContextType.Guild)]
[Priority(1)]
public async Task RaffleCur([OverrideTypeReader(typeof(BalanceTypeReader))] long amount, bool mixed = false)
{
if (!await CheckBetMandatory(amount))
return;
async Task OnEnded(IUser arg, long won)
{
await Response()
.Confirm(GetText(strs.rafflecur_ended(CurrencyName,
Format.Bold(arg.ToString()),
won + CurrencySign)))
.SendAsync();
}
var res = await _service.JoinOrCreateGame(ctx.Channel.Id, ctx.User, amount, mixed, OnEnded);
if (res.Item1 is not null)
{
await Response()
.Confirm(GetText(strs.rafflecur(res.Item1.GameType.ToString())),
string.Join("\n", res.Item1.Users.Select(x => $"{x.DiscordUser} ({N(x.Amount)})")),
footer: GetText(strs.rafflecur_joined(ctx.User.ToString())))
.SendAsync();
}
else
{
if (res.Item2 == CurrencyRaffleService.JoinErrorType.AlreadyJoinedOrInvalidAmount)
await Response().Error(strs.rafflecur_already_joined).SendAsync();
else if (res.Item2 == CurrencyRaffleService.JoinErrorType.NotEnoughCurrency)
await Response().Error(strs.not_enough(CurrencySign)).SendAsync();
}
}
}
}

View File

@@ -1,69 +0,0 @@
#nullable disable
namespace NadekoBot.Modules.Gambling.Common;
public class CurrencyRaffleGame
{
public enum Type
{
Mixed,
Normal
}
public IEnumerable<User> Users
=> _users;
public Type GameType { get; }
private readonly HashSet<User> _users = new();
public CurrencyRaffleGame(Type type)
=> GameType = type;
public bool AddUser(IUser usr, long amount)
{
// if game type is normal, and someone already joined the game
// (that's the user who created it)
if (GameType == Type.Normal && _users.Count > 0 && _users.First().Amount != amount)
return false;
if (!_users.Add(new()
{
DiscordUser = usr,
Amount = amount
}))
return false;
return true;
}
public User GetWinner()
{
var rng = new NadekoRandom();
if (GameType == Type.Mixed)
{
var num = rng.NextLong(0L, Users.Sum(x => x.Amount));
var sum = 0L;
foreach (var u in Users)
{
sum += u.Amount;
if (sum > num)
return u;
}
}
var usrs = _users.ToArray();
return usrs[rng.Next(0, usrs.Length)];
}
public class User
{
public IUser DiscordUser { get; set; }
public long Amount { get; set; }
public override int GetHashCode()
=> DiscordUser.GetHashCode();
public override bool Equals(object obj)
=> obj is User u ? u.DiscordUser == DiscordUser : false;
}
}

View File

@@ -1,81 +0,0 @@
#nullable disable
using NadekoBot.Modules.Gambling.Common;
namespace NadekoBot.Modules.Gambling.Services;
public class CurrencyRaffleService : INService
{
public enum JoinErrorType
{
NotEnoughCurrency,
AlreadyJoinedOrInvalidAmount
}
public Dictionary<ulong, CurrencyRaffleGame> Games { get; } = new();
private readonly SemaphoreSlim _locker = new(1, 1);
private readonly ICurrencyService _cs;
public CurrencyRaffleService(ICurrencyService cs)
=> _cs = cs;
public async Task<(CurrencyRaffleGame, JoinErrorType?)> JoinOrCreateGame(
ulong channelId,
IUser user,
long amount,
bool mixed,
Func<IUser, long, Task> onEnded)
{
await _locker.WaitAsync();
try
{
var newGame = false;
if (!Games.TryGetValue(channelId, out var crg))
{
newGame = true;
crg = new(mixed ? CurrencyRaffleGame.Type.Mixed : CurrencyRaffleGame.Type.Normal);
Games.Add(channelId, crg);
}
//remove money, and stop the game if this
// user created it and doesn't have the money
if (!await _cs.RemoveAsync(user.Id, amount, new("raffle", "join")))
{
if (newGame)
Games.Remove(channelId);
return (null, JoinErrorType.NotEnoughCurrency);
}
if (!crg.AddUser(user, amount))
{
await _cs.AddAsync(user.Id, amount, new("raffle", "refund"));
return (null, JoinErrorType.AlreadyJoinedOrInvalidAmount);
}
if (newGame)
{
_ = Task.Run(async () =>
{
await Task.Delay(60000);
await _locker.WaitAsync();
try
{
var winner = crg.GetWinner();
var won = crg.Users.Sum(x => x.Amount);
await _cs.AddAsync(winner.DiscordUser.Id, won, new("raffle", "win"));
Games.Remove(channelId, out _);
_ = onEnded(winner.DiscordUser, won);
}
catch { }
finally { _locker.Release(); }
});
}
return (crg, null);
}
finally
{
_locker.Release();
}
}
}

View File

@@ -23,9 +23,6 @@ public partial class Gambling
[Group]
public partial class SlotCommands : GamblingSubmodule<IGamblingService>
{
private static decimal totalBet;
private static decimal totalPaidOut;
private readonly IImageCache _images;
private readonly FontProvider _fonts;
private readonly DbService _db;
@@ -69,17 +66,19 @@ public partial class Gambling
var eb = _sender.CreateEmbed()
.WithAuthor(ctx.User)
.WithDescription(Format.Bold(text))
.WithImageUrl($"attachment://result.png")
.WithOkColor();
.WithAuthor(ctx.User)
.WithDescription(Format.Bold(text))
.WithImageUrl($"attachment://result.png")
.WithOkColor();
var bb = new ButtonBuilder(emote: Emoji.Parse("🔁"), customId: "slot:again", label: "Pull Again");
var inter = _inter.Create(ctx.User.Id, bb, smc =>
{
smc.DeferAsync();
return Slot(amount);
});
var inter = _inter.Create(ctx.User.Id,
bb,
smc =>
{
smc.DeferAsync();
return Slot(amount);
});
var msg = await ctx.Channel.SendFileAsync(imgStream,
"result.png",
@@ -159,12 +158,6 @@ public partial class Gambling
{
return null;
}
lock (_slotStatsLock)
{
totalBet += amount;
totalPaidOut += result.Won;
}
return result;
}
@@ -211,7 +204,7 @@ public partial class Gambling
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Origin = new(393, 480)
Origin = new(393, 480)
},
ownedAmount.ToString(),
fontColor));