Browse Source

Improve rollcoin logic to reduce race conditions, implement multi-gacha

Kirk Trombley 3 years ago
parent
commit
0877e589b9
1 changed files with 46 additions and 26 deletions
  1. 46 26
      commands/commands/rollcoin.py

+ 46 - 26
commands/commands/rollcoin.py

@@ -3,11 +3,12 @@ from typing import Optional
 from logging import Logger
 import random
 import json
+import asyncio
 
 from sudoku_py import SudokuGenerator, Sudoku
 
 from rollbot import as_command, initialize_data, RollbotFailure, Attachment
-from rollbot.injection import Data, Sender, Config, Const, Arg, Reply, OriginAdmin, Args, Attachments, Origin, Request
+from rollbot.injection import Data, Sender, Config, Const, Arg, Reply, OriginAdmin, Args, Attachments, Origin, Request, Lazy
 
 # View
 #   !wallet - shows your number of rollcoins, NFTs (Non-Functional Tamagotchis), and market balance
@@ -324,7 +325,7 @@ async def mine(
     admin: OriginAdmin,
     state: State,
     sender_id: Sender,
-    sender_wallet: SenderWallet,
+    get_sender_wallet: Lazy(SenderWallet),
     wallet_store: Data(RollcoinWallet),
     state_store: Data(RollcoinState),
     reply: Reply,
@@ -401,6 +402,7 @@ async def mine(
     difficulty = (zeroes - 10) / 4
     value = round(abs(random.gauss(difficulty * 200, 25)), 2)
 
+    sender_wallet = await get_sender_wallet()
     sender_wallet.balance += value
     await wallet_store.save(sender_id, sender_wallet)
     
@@ -418,7 +420,7 @@ async def appraise(
     origin: Origin,
     attachments: Attachments,
     sender_id: Sender,
-    sender_wallet: SenderWallet,
+    get_sender_wallet: Lazy(SenderWallet),
     wallet_store: Data(RollcoinWallet),
     state: State,
     state_store: Data(RollcoinState),
@@ -447,6 +449,7 @@ async def appraise(
         return "Sorry, I don't think that message is worth very much...", reply
 
     value = round(abs(random.gauss(score_base * 50, 15)), 2)
+    sender_wallet = await get_sender_wallet()
     sender_wallet.balance += value
     await wallet_store.save(sender_id, sender_wallet)
     state.appraised.append(appraisal_id)
@@ -466,23 +469,7 @@ def pull_gacha_color():
     return random.choice(NFT_COLORS) # nice%
 
 
-@as_command
-async def gacha(
-    sender_wallet: SenderWallet,
-    sender_id: Sender,
-    wallet_store: Data(RollcoinWallet),
-    logger: Logger,
-    req: Request,
-    name_api_key: NameAPIKey,
-    reply: Reply,
-):
-    """
-    Spend one RollCoin to pull from the gachapon! It dispenses the hottest new commodity - broken virtual pets from the 90s!
-    These "Non-Functional Tamagotchis" or "NFTs" have varying rarities - good luck!
-    """
-    if sender_wallet.balance < 1:
-        return f"You only have {sender_wallet.balance} RollCoins available, and gacha pulls cost one each!", reply
-
+async def pull_gacha(req, name_api_key):
     color1 = pull_gacha_color()
     color2 = pull_gacha_color()
 
@@ -504,15 +491,48 @@ async def gacha(
         color_info = f"{color1} and {color2}"
     rarity_info = NFT_MAX_RARITY if rarity >= len(NFT_RARITY) else NFT_RARITY[rarity]
 
-    info = f"{name} ({color_info}) ({rarity_info})"
+    return f"{name} ({color_info}) ({rarity_info})", img
 
-    sender_wallet.balance -= 1
-    sender_wallet.nfts.append(info)
-    await wallet_store.save(sender_id, sender_wallet)
-    
-    return f"You received...\n\t{info}", img, reply
 
+@as_command
+async def gacha(
+    sender_wallet: SenderWallet,
+    get_sender_wallet: Lazy(SenderWallet), # used for re-querying to mitigate race condition
+    sender_id: Sender,
+    wallet_store: Data(RollcoinWallet),
+    logger: Logger,
+    req: Request,
+    name_api_key: NameAPIKey,
+    pulls: Arg(0, convert=int, required=False, default=1, fail_msg="Number of pulls must be an integer"),
+    reply: Reply,
+):
+    """
+    Spend one RollCoin to pull from the gachapon! It dispenses the hottest new commodity - broken virtual pets from the 90s!
+    These "Non-Functional Tamagotchis" or "NFTs" have varying rarities - good luck!
+    """
+    if not (1 <= pulls <= 10):
+        RollbotFailure.INVALID_ARGUMENTS.raise_exc("Number of pulls must be between 1 and 10")
 
+    if sender_wallet.balance < pulls:
+        yield f"You only have {sender_wallet.balance} RollCoins available, and gacha pulls cost one each!", reply
+        return
+
+    info, img = await pull_gacha(req, name_api_key)
+    pulled = [info]
+    if pulls == 1:
+        yield f"You received...\n\t{info}", img, reply
+    else:
+        yield f"You received (1/{pulls})...\n\t{info}", img, reply
+        for i in range(pulls - 1):
+            await asyncio.sleep(1)
+            info, img = await pull_gacha(req, name_api_key)
+            yield f"You received ({i + 2}/{pulls})...\n\t{info}", img, reply
+            pulled.append(info)
+    
+    sender_wallet = await get_sender_wallet()
+    sender_wallet.balance -= pulls
+    sender_wallet.nfts += pulled
+    await wallet_store.save(sender_id, sender_wallet)
 
 # ADMIN COMMANDS