summaryrefslogtreecommitdiff
path: root/backend/main.py
diff options
context:
space:
mode:
authorAAGaming <aa@mail.catvibers.me>2022-11-15 16:44:24 -0500
committerGitHub <noreply@github.com>2022-11-15 13:44:24 -0800
commit50764600c83b2bdec599a9fd8e27b3c10a2afd96 (patch)
treef802b7c438723846971162d0c845caa237e0cb7d /backend/main.py
parentaec70631393ba307f1ca493a4d96f244e1439555 (diff)
downloaddecky-loader-50764600c83b2bdec599a9fd8e27b3c10a2afd96.tar.gz
decky-loader-50764600c83b2bdec599a9fd8e27b3c10a2afd96.zip
Refactoring in preparation for WebSockets (#254)v2.4.1-pre2
* Fix injector race conditions * add some more tasks * hide useless rollup warnings * goodbye to clientsession errors * completely fix desktop mode switch race condition * fix typos and TS warning in plugin error handler * fix chown error * start debugger if needed and not already started * fix get_steam_resource for the like 2 legacy plugins still using it lol * add ClientOSError to get_tabs error handling
Diffstat (limited to 'backend/main.py')
-rw-r--r--backend/main.py82
1 files changed, 47 insertions, 35 deletions
diff --git a/backend/main.py b/backend/main.py
index 7e3cdd2a..cd73ce26 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -4,7 +4,7 @@ from subprocess import call
if hasattr(sys, '_MEIPASS'):
call(['chmod', '-R', '755', sys._MEIPASS])
# Full imports
-from asyncio import get_event_loop, sleep
+from asyncio import new_event_loop, set_event_loop, sleep
from json import dumps, loads
from logging import DEBUG, INFO, basicConfig, getLogger
from os import getenv, chmod, path
@@ -12,7 +12,7 @@ from traceback import format_exc
import aiohttp_cors
# Partial imports
-from aiohttp import ClientSession, client_exceptions, WSMsgType
+from aiohttp import client_exceptions, WSMsgType
from aiohttp.web import Application, Response, get, run_app, static
from aiohttp_jinja2 import setup as jinja_setup
@@ -21,7 +21,7 @@ from browser import PluginBrowser
from helpers import (REMOTE_DEBUGGER_UNIT, csrf_middleware, get_csrf_token,
get_home_path, get_homebrew_path, get_user,
get_user_group, set_user, set_user_group,
- stop_systemd_unit)
+ stop_systemd_unit, start_systemd_unit)
from injector import get_gamepadui_tab, Tab, get_tabs
from loader import Loader
from settings import SettingsManager
@@ -56,15 +56,15 @@ basicConfig(
logger = getLogger("Main")
-async def chown_plugin_dir(_):
+async def chown_plugin_dir():
code_chown = call(["chown", "-R", USER+":"+GROUP, CONFIG["plugin_path"]])
code_chmod = call(["chmod", "-R", "555", CONFIG["plugin_path"]])
if code_chown != 0 or code_chmod != 0:
logger.error(f"chown/chmod exited with a non-zero exit code (chown: {code_chown}, chmod: {code_chmod})")
class PluginManager:
- def __init__(self) -> None:
- self.loop = get_event_loop()
+ def __init__(self, loop) -> None:
+ self.loop = loop
self.web_app = Application()
self.web_app.middlewares.append(csrf_middleware)
self.cors = aiohttp_cors.setup(self.web_app, defaults={
@@ -81,12 +81,19 @@ class PluginManager:
self.updater = Updater(self)
jinja_setup(self.web_app)
- if CONFIG["chown_plugin_path"] == True:
- self.web_app.on_startup.append(chown_plugin_dir)
- self.loop.create_task(self.loader_reinjector())
- self.loop.create_task(self.load_plugins())
- if not self.settings.getSetting("cef_forward", False):
- self.loop.create_task(stop_systemd_unit(REMOTE_DEBUGGER_UNIT))
+
+ async def startup(_):
+ if self.settings.getSetting("cef_forward", False):
+ self.loop.create_task(start_systemd_unit(REMOTE_DEBUGGER_UNIT))
+ else:
+ self.loop.create_task(stop_systemd_unit(REMOTE_DEBUGGER_UNIT))
+ if CONFIG["chown_plugin_path"] == True:
+ chown_plugin_dir()
+ self.loop.create_task(self.loader_reinjector())
+ self.loop.create_task(self.load_plugins())
+
+ self.web_app.on_startup.append(startup)
+
self.loop.set_exception_handler(self.exception_handler)
self.web_app.add_routes([get("/auth/token", self.get_auth_token)])
@@ -103,31 +110,29 @@ class PluginManager:
async def get_auth_token(self, request):
return Response(text=get_csrf_token())
- async def wait_for_server(self):
- async with ClientSession() as web:
- while True:
- try:
- await web.get(f"http://{CONFIG['server_host']}:{CONFIG['server_port']}")
- return
- except Exception as e:
- await sleep(0.1)
-
async def load_plugins(self):
- await self.wait_for_server()
+ # await self.wait_for_server()
+ logger.debug("Loading plugins")
self.plugin_loader.import_plugins()
# await inject_to_tab("SP", "window.syncDeckyPlugins();")
async def loader_reinjector(self):
while True:
tab = None
+ nf = False
+ dc = False
while not tab:
try:
tab = await get_gamepadui_tab()
except client_exceptions.ClientConnectorError or client_exceptions.ServerDisconnectedError:
- logger.debug("Couldn't connect to debugger, waiting 5 seconds.")
+ if not dc:
+ logger.debug("Couldn't connect to debugger, waiting...")
+ dc = True
pass
except ValueError:
- logger.debug("Couldn't find GamepadUI tab, waiting 5 seconds")
+ if not nf:
+ logger.debug("Couldn't find GamepadUI tab, waiting...")
+ nf = True
pass
if not tab:
await sleep(5)
@@ -136,15 +141,20 @@ class PluginManager:
await self.inject_javascript(tab, True)
try:
async for msg in tab.listen_for_message():
- logger.debug("Page event: " + str(msg.get("method", None)))
- if msg.get("method", None) == "Page.domContentEventFired":
- if not await tab.has_global_var("deckyHasLoaded", False):
- await self.inject_javascript(tab)
- if msg.get("method", None) == "Inspector.detached" or msg.get("type", None) in (WSMsgType.CLOSED, WSMsgType.ERROR):
- logger.info("CEF has disconnected...")
- logger.debug("Exit message: " + str(msg))
- await tab.close_websocket()
- break
+ # this gets spammed a lot
+ if msg.get("method", None) != "Page.navigatedWithinDocument":
+ logger.debug("Page event: " + str(msg.get("method", None)))
+ if msg.get("method", None) == "Page.domContentEventFired":
+ if not await tab.has_global_var("deckyHasLoaded", False):
+ await self.inject_javascript(tab)
+ if msg.get("method", None) == "Inspector.detached":
+ logger.info("CEF has requested that we detach.")
+ await tab.close_websocket()
+ break
+ # If this is a forceful disconnect the loop will just stop without any failure message. In this case, injector.py will handle this for us so we don't need to close the socket.
+ # This is because of https://github.com/aio-libs/aiohttp/blob/3ee7091b40a1bc58a8d7846e7878a77640e96996/aiohttp/client_ws.py#L321
+ logger.info("CEF has disconnected...")
+ # At this point the loop starts again and we connect to the freshly started Steam client once it is ready.
except Exception as e:
logger.error("Exception while reading page events " + format_exc())
await tab.close_websocket()
@@ -166,7 +176,7 @@ class PluginManager:
# logger.debug("Closing tab: " + getattr(t, "title", "Untitled"))
# await t.close()
# await sleep(0.5)
- await tab.evaluate_js("try{if (window.deckyHasLoaded){setTimeout(() => SteamClient.User.StartRestart(), 100)}else{window.deckyHasLoaded = true;(async()=>{while(!window.SP_REACT){await new Promise(r => setTimeout(r, 10))};await import('http://localhost:1337/frontend/index.js')})();}}catch(e){console.error(e)}", False, False, False)
+ await tab.evaluate_js("try{if (window.deckyHasLoaded){setTimeout(() => SteamClient.User.StartRestart(), 100)}else{window.deckyHasLoaded = true;(async()=>{try{while(!window.SP_REACT){await new Promise(r => setTimeout(r, 10))};await import('http://localhost:1337/frontend/index.js')}catch(e){console.error(e)};})();}}catch(e){console.error(e)}", False, False, False)
except:
logger.info("Failed to inject JavaScript into tab\n" + format_exc())
pass
@@ -175,4 +185,6 @@ class PluginManager:
return run_app(self.web_app, host=CONFIG["server_host"], port=CONFIG["server_port"], loop=self.loop, access_log=None)
if __name__ == "__main__":
- PluginManager().run()
+ loop = new_event_loop()
+ set_event_loop(loop)
+ PluginManager(loop).run()