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 FROM store_item") items = {item[0]:item[1:] for item in cursor.fetchall()} cursor.close() return items # 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, 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_stakes(): cursor = conn.cursor() cursor.execute(""" SELECT staking_event.id,well_id,inventory_item_id,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 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() cursor.execute(""" INSERT INTO staking_event (well_id, inventory_item_id, duration_in_mins) VALUES (?, ?, ?) """, (well_id, item_id, 3)) conn.commit() world.staking_sources = get_moons() world.stakes = get_stakes() cursor.close() def claim(staking_event_id): cursor = conn.cursor() cursor.execute("INSERT INTO claim_event (claim_amount, staking_event_id) VALUES (?, ?)", (10, staking_event_id)) conn.commit() world.staking_sources = get_moons() world.stakes = get_stakes() 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 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}") def draw_store(): for id,(name,price,claim) 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}") 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: 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,(_,invId,dur,start) in stakes.items(): start_time = parse(start) now = datetime.datetime.utcnow().replace(microsecond=0) delta = now - start_time im.push_id(f"Claim{stake_id}") if delta > datetime.timedelta(minutes=dur): if im.button("Claim"): claim(stake_id) else: im.text_disabled("Claim") im.pop_id() im.text(f"{world.store[world.inventory[invId][0]][0]}: {delta}") 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() 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()