from dateutil.parser import parse from dataclasses import dataclass import sqlite3 as sql import PySimpleGUI as sg from ImguiWindow import ImguiWindow, load_texture import glfw import imgui as im import random import datetime def get_bank(): cursor = conn.cursor() cursor.execute(""" SELECT resource_account.id,resource.name,balance FROM resource_account INNER JOIN resource ON resource.id = resource_account.resource_id WHERE user_id = ? """, (world.current_user_id,)) resources = {id:(name, balance) for id,name,balance in cursor.fetchall()} cursor.execute("SELECT bank_account.id,balance FROM bank_account WHERE user_id = ?", (world.current_user_id,)) bank = cursor.fetchone() cursor.close() return (bank, resources) def get_store_items(): cursor = conn.cursor() cursor.execute(""" SELECT store_item.id, store_item.name, price, claim_amount, completion_time_mins FROM store_item """) items = {item[0]:item[1:] for item in cursor.fetchall()} cursor.close() return items def get_stakes(): cursor = conn.cursor() cursor.execute(""" SELECT staking_event.id, well_id, inventory_item_id, stake_amount, duration_in_mins, staking_event.created_at, CASE WHEN claim_event.staking_event_id IS NULL THEN 1 ELSE 0 END AS is_active FROM staking_event INNER JOIN resource_well ON resource_well.id = staking_event.well_id INNER JOIN staking_source ON staking_source.id = resource_well.source_id LEFT JOIN claim_event ON claim_event.staking_event_id = staking_event.id WHERE staking_source.user_id = ?; """, (world.current_user_id,)) stakes = {item[0]:(item[1],item[2],item[3],item[4],bool(item[-1])) for item in cursor.fetchall()} cursor.close() return stakes # An active stake is basically defined as one that has yet to be claimed def get_active_stakes(source_id): cursor = conn.cursor() cursor.execute(""" SELECT staking_event.id, well_id, inventory_item_id, stake_amount, duration_in_mins, staking_event.created_at FROM staking_event LEFT JOIN claim_event ON staking_event.id = claim_event.staking_event_id INNER JOIN resource_well ON resource_well.id = staking_event.well_id WHERE resource_well.source_id = ? AND claim_event.staking_event_id IS NULL; """, (source_id,)) active_stakes = {item[0]:item[1:] for item in cursor.fetchall()} cursor.close() return active_stakes def get_wells(source_id): cursor = conn.cursor() cursor.execute(""" SELECT resource_well.id,name,supply,staking_event.created_at FROM resource_well INNER JOIN resource ON resource.id = resource_well.resource_id LEFT JOIN staking_event ON staking_event.well_id = resource_well.id WHERE resource_well.source_id = ?; """, (source_id,)) wells = {id: (name,supply,timestamp) for id,name,supply,timestamp in cursor.fetchall()} cursor.close() return wells def get_all_wells(): cursor = conn.cursor() cursor.execute(""" SELECT resource_well.id,name,supply FROM resource_well INNER JOIN resource ON resource.id = resource_well.resource_id INNER JOIN staking_source ON staking_source.id = resource_well.source_id WHERE staking_source.user_id = ?; """, (world.current_user_id,)) wells = {w[0]:w[1:] for w in cursor.fetchall()} cursor.close() return wells def get_moons(): cursor = conn.cursor() cursor.execute("SELECT id,created_at FROM staking_source WHERE user_id = ?", (world.current_user_id,)) staking_sources = {} for sid,ts in cursor.fetchall(): staking_sources[sid] = (ts, get_wells(sid), get_active_stakes(sid)) cursor.close() return staking_sources def get_inventory(): cursor = conn.cursor() cursor.execute(""" SELECT inventory_item.id,store_item_id, COUNT(upgrade_event.id) as upgrades FROM inventory_item LEFT JOIN upgrade_event ON inventory_item.id = upgrade_event.inventory_item_id WHERE inventory_item.user_id = ? GROUP BY inventory_item.id; """, (world.current_user_id,)) inventory = {item[0]:item[1:] for item in cursor.fetchall()} cursor.close() return inventory def mint(): cursor = conn.cursor() rand_hash = "%010x" % random.randrange(16 ** 16) cursor.execute('BEGIN') source_id = -1 try: cursor.execute(""" INSERT INTO staking_source (user_id, address) VALUES (?, ?) """, (world.current_user_id, f"0x{rand_hash}")) source_id = cursor.lastrowid for id,_ in world.resources: init_supply = random.randint(50, 200) cursor.execute(""" INSERT INTO resource_well (source_id, resource_id, supply) VALUES (?, ?, ?) """, (source_id, id, init_supply)) conn.commit() except sql.Error as error: print(error) conn.rollback() finally: cursor.close() world.staking_sources = get_moons() return source_id def buy_item(item_id): cursor = conn.cursor() cost = world.store[item_id][1] if cost > world.bank[0][1]: print(f"Not enough money: Bank {world.bank[0][1]} < Cost {cost}") return -1 cursor.execute('BEGIN') try: cursor.execute(""" INSERT INTO inventory_item (user_id, store_item_id) VALUES (?, ?) """, (world.current_user_id, item_id)) cursor.execute(""" UPDATE bank_account SET balance = balance - ? WHERE bank_account.id = ? """, (cost, world.bank[0][0])) except sql.Error as error: conn.rollback() finally: item_id = cursor.lastrowid conn.commit() cursor.close() world.inventory = get_inventory() world.bank = get_bank() return item_id def sell_item(item_id): cursor = conn.cursor() cost = world.store[world.inventory[item_id][0]][1] cursor.execute('BEGIN') try: # TODO: We have to cascade delete stakes and claims after this cursor.execute("DELETE FROM inventory_item WHERE user_id = ? AND id = ?", (world.current_user_id, item_id)) cursor.execute(""" UPDATE bank_account SET balance = balance + ? WHERE bank_account.id = ? """, (cost, world.bank[0][0])) except sql.Error as error: conn.rollback() finally: conn.commit() cursor.close() world.inventory = get_inventory() world.bank = get_bank() def mine(well_id, item_id): cursor = conn.cursor() store_item = world.store[world.inventory[item_id][0]] cursor.execute(""" INSERT INTO staking_event (well_id, inventory_item_id, stake_amount, duration_in_mins) VALUES (?, ?, ?, ?) """, (well_id, item_id, store_item[2], store_item[3])) conn.commit() world.staking_sources = get_moons() world.stakes = get_stakes() cursor.close() def claim(staking_event_id): cursor = conn.cursor() claim_amount = world.stakes[staking_event_id][2] well_id = world.stakes[staking_event_id][0] try: cursor.execute("SELECT supply FROM resource_well WHERE id = ?", (well_id,)) supply = cursor.fetchone()[0] claim_amount = claim_amount if supply >= claim_amount else supply print(claim_amount) cursor.execute(""" UPDATE resource_well SET supply = supply - ? WHERE resource_well.id = ? """, (claim_amount, well_id)) cursor.execute("INSERT INTO claim_event (claim_amount, staking_event_id) VALUES (?, ?)", (claim_amount, staking_event_id)) cursor.execute(""" WITH w_res_id AS (SELECT resource_id FROM resource_well WHERE id = ?) UPDATE resource_account SET balance = balance + ? FROM w_res_id WHERE resource_account.user_id = ? AND resource_account.resource_id = w_res_id.resource_id """, (well_id, claim_amount, world.current_user_id)) except sql.Error as error: print(error) conn.rollback() finally: conn.commit() world.stakes = get_stakes() world.staking_sources = get_moons() world.bank = get_bank() cursor.close() def upgrade(item_id): cursor = conn.cursor() cursor.execute("INSERT INTO upgrade_event (inventory_item_id) VALUES (?)", (item_id,)) conn.commit() world.inventory = get_inventory() cursor.close() def destroy(source_id): cursor = conn.cursor() cursor.execute("DELETE FROM staking_source WHERE id = ?", (source_id,)) conn.commit() cursor.close() world.staking_sources = get_moons() def sell_all(): cursor = conn.cursor() cursor.execute("DELETE FROM inventory_item WHERE user_id = ?", (current_user_id,)) conn.commit() cursor.close() world.inventory = get_inventory() def trade(): cursor = conn.cursor() cursor.execute(""" UPDATE resource_account SET balance = balance - ? WHERE resource_account.user_id = ? """, (10, world.current_user_id)) cursor.execute(""" UPDATE bank_account SET balance = balance + ? WHERE bank_account.user_id = ? """, (1, world.current_user_id)) conn.commit() cursor.close() world.bank = get_bank() def draw_dashboard(): im.text(f"Current User: {world.current_user}") for _ in range(5): im.spacing() im.text(f"Moon Bucks: {world.bank[0][1]}") for _ in range(10): im.spacing() for id,(name,balance) in world.bank[1].items(): im.text(f"{name.capitalize()}: {balance}") for _ in range(10): im.spacing() resources = world.bank[1] can_sell = True for _,supply in resources.values(): if supply < 10: can_sell = False im.text("Sell 10 resources for 1 Moon Bucks") if can_sell: if im.button("Sell"): trade() else: im.text("Sell (Not enough funds)") def draw_store(): for id,(name,price,claim,duration) in world.store.items(): owned = False for (store_item_id,_) in world.inventory.values(): if id == store_item_id: owned = True im.text(f"{name}: Mine {claim} in {duration} mins") if owned: im.text_disabled(f"Buy {price}") else: im.push_id(f"Buy{id}") if im.button(f"Buy {price}"): buy_item(id) im.pop_id() for _ in range(5): im.spacing() def draw_inventory(): for id,(sid,tier) in world.inventory.items(): im.text(f"{world.store[sid][0]} - Tier {tier+1}") im.push_id(f"Upgrade{id}") if im.button("Upgrade"): upgrade(id) im.pop_id() im.same_line() im.push_id(f"Sell{id}") if im.button("Sell"): sell_item(id) im.pop_id() for _ in range(5): im.spacing() def draw_moons(): if im.button("Mint"): mint() im.begin_child("Moons") im.columns(3, 'fileLlist') im.set_column_width(0, 250) im.set_column_width(1, 600) im.separator() im.text("Moon") im.next_column() im.text("Resources") im.next_column() im.text("Active Stakes") im.next_column() im.separator() for source_id,(ts,wells,stakes) in world.staking_sources.items(): im.image(world.moon_img_tex_id, 240, 200) im.push_id(f"Destroy{id}") if im.button("Destroy"): destroy(source_id) im.pop_id() im.next_column() for well_id,(name,supply,ts) in wells.items(): im.text(f"{name.capitalize()}: {supply}") for item_id,(store_id,_) in world.inventory.items(): item_name = world.store[store_id][0] skip = False for ss in world.stakes.values(): if ss[1] == item_id and ss[4] == True or supply == 0: skip = True if skip == True: continue im.push_id(f"Mine{well_id}") if im.button(item_name): mine(well_id, item_id) im.pop_id() im.same_line() im.spacing() im.next_column() for stake_id,(well_id,invId,amount,dur,start) in stakes.items(): start_time = parse(start) now = datetime.datetime.utcnow().replace(microsecond=0) elapsed = now - start_time remaining = start_time + datetime.timedelta(minutes=dur) - now btn_txt = f"Claim {amount} {world.wells[well_id][0].capitalize()}" if elapsed > datetime.timedelta(minutes=dur): im.push_id(f"Claim{stake_id}") if im.button(btn_txt): claim(stake_id) im.pop_id() im.text(f"Finished {elapsed} ago") else: im.text_disabled(btn_txt) im.text(f"{remaining} left") im.next_column() im.separator() im.columns(1) im.end_child() def draw_panels(): screen_width, screen_height = glfw.get_video_mode(glfw.get_primary_monitor())[0] # TODO: We probably don't need to create a "Main" window... im.set_next_window_size(screen_width, screen_height) # Set the next window position to (0, 0) to make the window cover the entire screen im.set_next_window_position(0, 0) f = im.WINDOW_NO_TITLE_BAR | im.WINDOW_NO_RESIZE | im.WINDOW_NO_MOVE | im.WINDOW_NO_COLLAPSE im.begin("Main", flags=f) im.end() # Dashboard im.set_next_window_position(0, 0) im.set_next_window_size(400, 450) f = im.WINDOW_NO_RESIZE | im.WINDOW_NO_MOVE im.begin("Dashboard", flags=f) draw_dashboard() im.end() # Store im.set_next_window_position(402, 0) im.set_next_window_size(500, 450) f = im.WINDOW_NO_RESIZE | im.WINDOW_NO_MOVE im.begin("Store", flags=f) draw_store() im.end() # Inventory im.set_next_window_position(902, 0) im.set_next_window_size(500, 450) f = im.WINDOW_NO_RESIZE | im.WINDOW_NO_MOVE im.begin("Inventory", flags=f) draw_inventory() im.end() # Moons im.set_next_window_position(0, 452) im.set_next_window_size(1400, 540) f = im.WINDOW_NO_RESIZE | im.WINDOW_NO_MOVE | im.WINDOW_ALWAYS_VERTICAL_SCROLLBAR im.begin("Moons", flags=f) draw_moons() im.end() @dataclass class World: current_user = "Joe" current_user_id = 1 moon_img_tex_id = -1 bank = dict() resources = [] id_to_resource = dict() resource_to_id = dict() store = dict() inventory = dict() staking_sources = dict() stakes = dict() world = World() conn = sql.connect("mm.db", check_same_thread=False) conn.execute('PRAGMA foreign_keys = ON') world.current_user = "Joe" world.current_user_id = 1 cursor = conn.cursor() cursor.execute("SELECT id,name FROM resource") world.resources = cursor.fetchall() cursor.close() world.id_to_resource = {id: name for id, name in world.resources} world.resource_to_id = {name: id for id, name in world.resources} world.bank = get_bank() world.store = get_store_items() world.inventory = get_inventory() world.staking_sources = get_moons() world.stakes = get_stakes() world.wells = get_all_wells() def imgui_init(): world.moon_img_tex_id = texture_id = load_texture("moon.png") imguiWindow = ImguiWindow(imgui_init, draw_panels, "Moon Miner Test", 1400, 1000) conn.close()