Use gambler ID in Redis key to avoid the possibility of concurrent games messing with the state

This commit is contained in:
barelyprofessional
2026-02-07 11:27:03 -06:00
parent 54d989f64f
commit 8246b75868
2 changed files with 16 additions and 17 deletions

View File

@@ -59,12 +59,11 @@ public class MinesCommand : ICommand
true, autoDeleteAfter: gameDisabledCleanupDelay); true, autoDeleteAfter: gameDisabledCleanupDelay);
return; return;
} }
KasinoMines = new KasinoMines(botInstance);
var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx);
if (gambler == null) if (gambler == null)
throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}");
KasinoMines = new KasinoMines(botInstance, gambler.Id);
bool cashout = false; bool cashout = false;
if (message.Message.Contains("cashout")) cashout = true; if (message.Message.Contains("cashout")) cashout = true;
//check if user has an existing game already //check if user has an existing game already

View File

@@ -217,7 +217,7 @@ public class KasinoMines
} }
public KasinoMines(ChatBot kfChatBot) public KasinoMines(ChatBot kfChatBot, int gamblerId)
{ {
_kfChatBot = kfChatBot; _kfChatBot = kfChatBot;
var connectionString = SettingsProvider.GetValueAsync(BuiltIn.Keys.BotRedisConnectionString).Result; var connectionString = SettingsProvider.GetValueAsync(BuiltIn.Keys.BotRedisConnectionString).Result;
@@ -229,25 +229,25 @@ public class KasinoMines
var redis = ConnectionMultiplexer.Connect(connectionString.Value); var redis = ConnectionMultiplexer.Connect(connectionString.Value);
_redisDb = redis.GetDatabase(); _redisDb = redis.GetDatabase();
GetSavedGames().Wait(); GetSavedGames(gamblerId).Wait();
} }
public async Task RefreshGameMessage(int gamblerId) public async Task RefreshGameMessage(int gamblerId)
{ {
await GetSavedGames(); await GetSavedGames(gamblerId);
var game = ActiveGames[gamblerId]; var game = ActiveGames[gamblerId];
game.LastInteracted = DateTimeOffset.UtcNow; game.LastInteracted = DateTimeOffset.UtcNow;
var msg = await _kfChatBot.SendChatMessageAsync($"{game.ToString()}", true); var msg = await _kfChatBot.SendChatMessageAsync($"{game.ToString()}", true);
await _kfChatBot.WaitForChatMessageAsync(msg); await _kfChatBot.WaitForChatMessageAsync(msg);
await game.ResetMessage(msg); await game.ResetMessage(msg);
ActiveGames[gamblerId] = game; ActiveGames[gamblerId] = game;
await SaveActiveGames(); await SaveActiveGames(gamblerId);
} }
public async Task GetSavedGames() public async Task GetSavedGames(int gamblerId)
{ {
if (_redisDb == null) throw new InvalidOperationException("Kasino mines service isn't initialized"); if (_redisDb == null) throw new InvalidOperationException("Kasino mines service isn't initialized");
var json = await _redisDb.StringGetAsync("Mines.State"); var json = await _redisDb.StringGetAsync($"Mines.State.{gamblerId}");
if (string.IsNullOrEmpty(json)) return; if (string.IsNullOrEmpty(json)) return;
try try
{ {
@@ -261,18 +261,18 @@ public class KasinoMines
ActiveGames = new Dictionary<int, KasinoMinesGame>(); ActiveGames = new Dictionary<int, KasinoMinesGame>();
} }
} }
public async Task SaveActiveGames() public async Task SaveActiveGames(int gamblerId)
{ {
if (_redisDb == null) throw new InvalidOperationException("Kasino mines service isn't initialized"); if (_redisDb == null) throw new InvalidOperationException("Kasino mines service isn't initialized");
var json = JsonSerializer.Serialize(ActiveGames); var json = JsonSerializer.Serialize(ActiveGames);
await _redisDb.StringSetAsync("Mines.State", json, null, When.Always); await _redisDb.StringSetAsync($"Mines.State.{gamblerId}", json, null, When.Always);
} }
public async Task RemoveGame(int gamblerId) public async Task RemoveGame(int gamblerId)
{ {
await GetSavedGames(); await GetSavedGames(gamblerId);
ActiveGames?.Remove(gamblerId); ActiveGames?.Remove(gamblerId);
await SaveActiveGames(); await SaveActiveGames(gamblerId);
} }
public async Task Cashout(KasinoMinesGame game) public async Task Cashout(KasinoMinesGame game)
@@ -294,7 +294,7 @@ public class KasinoMines
public async Task<bool> Bet(int gamblerId, int count, SentMessageTrackerModel msg, bool cashOut = false) //returns false if you hit a bomb, true if you didn't public async Task<bool> Bet(int gamblerId, int count, SentMessageTrackerModel msg, bool cashOut = false) //returns false if you hit a bomb, true if you didn't
{ {
await GetSavedGames(); await GetSavedGames(gamblerId);
var game = ActiveGames[gamblerId]; var game = ActiveGames[gamblerId];
game.LastInteracted = DateTimeOffset.UtcNow; game.LastInteracted = DateTimeOffset.UtcNow;
if (game.LastMessageId != msg.ChatMessageId!.Value) if (game.LastMessageId != msg.ChatMessageId!.Value)
@@ -314,7 +314,7 @@ public class KasinoMines
public async Task<bool> Bet(int gamblerId, List<(int r, int c)> coords, SentMessageTrackerModel msg, bool cashOut = false) public async Task<bool> Bet(int gamblerId, List<(int r, int c)> coords, SentMessageTrackerModel msg, bool cashOut = false)
{ {
await GetSavedGames(); await GetSavedGames(gamblerId);
var game = ActiveGames[gamblerId]; var game = ActiveGames[gamblerId];
game.LastInteracted = DateTimeOffset.UtcNow; game.LastInteracted = DateTimeOffset.UtcNow;
if (game.LastMessageId != msg.ChatMessageId!.Value) if (game.LastMessageId != msg.ChatMessageId!.Value)
@@ -362,15 +362,15 @@ public class KasinoMines
ActiveGames[gamblerId] = game; ActiveGames[gamblerId] = game;
if (cashOut) await Cashout(game); if (cashOut) await Cashout(game);
else await SaveActiveGames(); else await SaveActiveGames(gamblerId);
return true; return true;
} }
public async Task CreateGame(GamblerDbModel gambler, decimal bet, int size, int mines) public async Task CreateGame(GamblerDbModel gambler, decimal bet, int size, int mines)
{ {
await GetSavedGames(); await GetSavedGames(gambler.Id);
ActiveGames.Add(gambler.Id, new KasinoMinesGame(gambler, bet, size, mines)); ActiveGames.Add(gambler.Id, new KasinoMinesGame(gambler, bet, size, mines));
await SaveActiveGames(); await SaveActiveGames(gambler.Id);
} }
} }