diff --git a/README.md b/README.md index 3b344e1..2a39435 100644 --- a/README.md +++ b/README.md @@ -22,55 +22,87 @@ Aurora is a set of utilities aiming to take PC couch gaming to the next level! - Update MSYS2 and install dependencies: ```sh -pacman -Syuu && pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_image mingw-w64-x86_64-SDL2_ttf +pacman -Syuu +pacman -S python3 python3-pip mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_image mingw-w64-x86_64-SDL2_ttf ``` +- Install python dependencies + +```sh +cd utilities +pip3 install -r requirements.txt +cd ../server +pip3 install -r requirements.txt +``` + +--- + ### Linux: + - Clone the repository - Install dependencies: ```sh -sudo apt install cmake g++ gcc libsdl2-dev libsdl2-2.0-0 libsdl2-image-dev libsdl2-image-2.0-0 libsdl2-ttf-dev libsdl2-ttf-2.0-0 +sudo apt install python3 python3-pip cmake g++ gcc libsdl2-dev libsdl2-2.0-0 libsdl2-image-dev libsdl2-image-2.0-0 libsdl2-ttf-dev libsdl2-ttf-2.0-0 +``` + +- Install python dependencies + +```sh +cd utilities +pip3 install -r requirements.txt +cd ../server +pip3 install -r requirements.txt ``` +--- + - **Fix for Ubuntu 21.04:** ```sh sudo apt install libudev1=247.3-3ubuntu3.1 ``` +--- + ### Mac OS: -Sorry but you're out of luck... -If you really want to try Aurora try to run it in a virtual machine with Linux! +#### You are going to need to have [Homebrew](https://brew.sh/) installed + +- Install dependencies -## :rocket: Compilation +```sh +brew install python3 sdl2 sdl2_image sdl2_ttf +``` -#### To compile (and run) Aurora, you will have to use the `./compile.sh` script. +- Clone the repository +- Install python dependencies ```sh -cd aurora -./compile.sh (args) +cd utilities +pip3 install -r requirements.txt +cd ../server +pip3 install -r requirements.txt ``` -Build environments: +--- -- `debug` -- `release` +## :rocket: Development -Arguments +```sh +./develop.sh +``` +#### This is just an alias to `python3 launcher/development.py` -- `-v` - Verbose mode (more debugging yay :cry:) -- `-dontRun` - By default, running the compile script will also run Aurora. If you want to stop that behaviour pass this argument. -- `-zip` - This will make a portable **.zip** file of the whole installation. -#### Example: +### Script Arguments: -Let's say you want to compile and run the debug environment with some extra info in case something breaks :boom: +| Argument | Short Argument | Description | +|----------|----------------|--------------| +|--verbose|-v|Verbose mode. Provides more debugging logs from mostly **cmake**| +|--port **PORT**|-p|The TCP port used for the internal server/client communication **(Default: 8000)**| +|--env **ENV**|-e|The environment to compile for. Mostly used for the client. Options: **"debug"** or **"release"**. **(Default: debug)** -```sh -./compile.sh debug -v -``` ## :hammer_and_wrench: Support diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index e2d7989..fb3b809 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -9,12 +9,17 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug") endif() message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") - + file(GLOB_RECURSE SRC_LIST src/*.cpp src/*.c include/*.hpp include/*.h) add_executable(${PROJECT_NAME} ${SRC_LIST}) target_include_directories(${PROJECT_NAME} PRIVATE "include") +# For mac os +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + target_include_directories(${PROJECT_NAME} PRIVATE "/usr/local/include") +endif() + # For windows networking IF(WIN32) target_link_libraries(${PROJECT_NAME} PRIVATE -lwsock32 -lWs2_32) @@ -32,7 +37,14 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) target_compile_features(${PROJECT_NAME} PRIVATE c_std_11) find_package(SDL2 REQUIRED) -target_link_libraries(${PROJECT_NAME} PRIVATE SDL2) + +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + target_link_libraries(${PROJECT_NAME} PRIVATE ${SDL_LIBRARY}) + target_link_libraries(${PROJECT_NAME} PRIVATE SDL2::SDL2) +else() + target_link_libraries(${PROJECT_NAME} PRIVATE SDL2) +endif() + # Fixes WinMain bug add_definitions(-DSDL_MAIN_HANDLED) diff --git a/compile.sh b/client/compile-deprecated.sh old mode 100644 new mode 100755 similarity index 86% rename from compile.sh rename to client/compile-deprecated.sh index 54f2538..ccd2983 --- a/compile.sh +++ b/client/compile-deprecated.sh @@ -1,6 +1,11 @@ #!/bin/bash # Tested with (Ubuntu 21.04 w/ G++, Windows 10 w/ MSYS MinGW64) + +# This script is old, please don't use it (Use the updated python one on ../launcher). +# I left it on purpose in the repo just to have as a guide if you're going down the path of trying to manually compile! +# On that note, good luck! + RED_COLOR='\033[0;31m' GREEN_COLOR='\033[1;32m' BLUE_COLOR='\033[1;34m' @@ -42,11 +47,11 @@ fi #clear -CLIENT_FOLDER_ENV="client/build-debug" +CLIENT_FOLDER_ENV="build-debug" ENV="Debug" if [[ "$1" == "release" ]]; then # Checks first argument: Compilation Environment - Release - CLIENT_FOLDER_ENV="client/build-release" + CLIENT_FOLDER_ENV="build-release" ENV="Release" fi diff --git a/client/src/main.cpp b/client/src/main.cpp index 47e9976..4f0c87f 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -25,10 +25,8 @@ int main(int argc, char* args[]) { if(!TTF_WasInit() && TTF_Init() == -1) { std::cout << "ERROR: TTF_Init has failed. TTF_ERROR: " << TTF_GetError() << std::endl; } - - RenderWindow window("Aurora v0.3.0", 1920, 1080); - //window.setFullScreen(true); + RenderWindow window("Aurora v0.3.1", 1280, 720); int windowRefreshRate = window.getRefreshRate(); @@ -48,15 +46,6 @@ int main(int argc, char* args[]) { int normalTransitionTime, spamTransitionTime, gameTitleFontSize; std::string gameFontFamilyName; - - auto createGameEntities = [&] (int num_of_entities) { - for (int i = 0; i < num_of_entities; i++) { - { - - } - } - }; - auto createGameEntity = [&] (int i, std::string name) { const MultiSize normalGameDims(Size(i*(gameSizeNormal + (marginBetweenGames * 2) + selectedGameOffset) + gameOffset, SIZE_HEIGHT), Size(normalY, SIZE_HEIGHT), Size(gameSizeNormal, SIZE_HEIGHT), Size(gameSizeNormal, SIZE_HEIGHT) ); const MultiSize selectedGameDims(Size(gameOffset - selectedGameOffset, SIZE_HEIGHT), Size(normalY, SIZE_HEIGHT), Size(gameSizeSelected, SIZE_HEIGHT), Size(gameSizeSelected, SIZE_HEIGHT) ); @@ -69,7 +58,7 @@ int main(int argc, char* args[]) { auto requestSteamGamesFromServer = [&] () { try { - http::Request request{"http://127.0.0.1:8000/steam"}; + http::Request request{"http://127.0.0.1:" + std::string(args[1]) + "/steam"}; const auto response = request.send("GET"); const std::string response_str = std::string{response.body.begin(), response.body.end()}; @@ -122,7 +111,7 @@ int main(int argc, char* args[]) { int prevSelectedGame = 0; Vector2f windowSize = window.getWindowDimensions(); - std::cout << "Width: " << windowSize.x << ", Height: " << windowSize.y << std::endl << std::endl; + std::cout << "Width: " << windowSize.x << ", Height: " << windowSize.y << std::endl; auto setGameTitleFont = [&] () { font = TTF_OpenFont(gameFontFamilyName.c_str(), gameTitleFontSize); @@ -188,10 +177,10 @@ int main(int argc, char* args[]) { while(SDL_PollEvent(&event)){ switch(event.type) { case SDL_QUIT: - readGameStyles(); - animateGames(); - setGameTitleFont(); - //isGameRunning = false; + // readGameStyles(); + // animateGames(); + // setGameTitleFont(); + isGameRunning = false; break; case SDL_JOYAXISMOTION: diff --git a/client/src/renderWindow.cpp b/client/src/renderWindow.cpp index 9b10c42..fcca61d 100644 --- a/client/src/renderWindow.cpp +++ b/client/src/renderWindow.cpp @@ -7,12 +7,18 @@ #include "Text.hpp" #include "Math.hpp" +#ifdef __APPLE__ + #define RENDERING_ARGS NULL +#else + #define RENDERING_ARGS SDL_WINDOW_VULKAN +#endif + float calculateSize(Size value, Vector2f windowDimensions) { return (value.val / 100) * (value.type == SIZE_HEIGHT ? windowDimensions.y : windowDimensions.x); } RenderWindow::RenderWindow(const char* p_title, int p_width, int p_height) :window(NULL), renderer(NULL), windowDims(Vector2f(p_width, p_height)) { - window = SDL_CreateWindow(p_title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, p_width, p_height, SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN );//| SDL_WINDOW_FULLSCREEN_DESKTOP ); + window = SDL_CreateWindow(p_title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, p_width, p_height, SDL_WINDOW_SHOWN | RENDERING_ARGS );//| SDL_WINDOW_FULLSCREEN_DESKTOP ); if(window == NULL) { std::cout << "Window has failed to init. Error: " << SDL_GetError() << std::endl; @@ -32,7 +38,7 @@ RenderWindow::RenderWindow(const char* p_title, int p_width, int p_height) :wind std::cout << "Fallback renderer has failed to init. Error: " << SDL_GetError() << std::endl; } - SDL_ShowCursor(false); // Hides the cursor; + // SDL_ShowCursor(false); // Hides the cursor; }; void RenderWindow::cleanUp() { diff --git a/develop.sh b/develop.sh new file mode 100755 index 0000000..7de2114 --- /dev/null +++ b/develop.sh @@ -0,0 +1,3 @@ +#!/bin/bash +python3 launcher/development.py "$@" + diff --git a/launcher/.gitignore b/launcher/.gitignore new file mode 100644 index 0000000..c7d59f9 --- /dev/null +++ b/launcher/.gitignore @@ -0,0 +1,152 @@ +*.log + +# Byte-compiled / optimized / DLL files +__pycache__/ +Helpers/__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject +### VirtualEnv template +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +.Python +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +.venv +pip-selfcheck.json +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +.idea/ + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties \ No newline at end of file diff --git a/launcher/Helpers/client.py b/launcher/Helpers/client.py new file mode 100644 index 0000000..994a2ec --- /dev/null +++ b/launcher/Helpers/client.py @@ -0,0 +1,122 @@ +from datetime import datetime +import subprocess +import os +import sys +from pathlib import Path +import logging + +# Constants +from Helpers.constants import CLIENT_DIR, MSYS2_MINGW64_EXECUTABLE, TIME_TO_WAIT_IF_IT_DIDNT_FIND_CMAKE_TASK +from Helpers.logger import make_logger + +logger = make_logger('Launcher') + +def run_cmake_command(args_list): + if os.name == 'nt': + cmake_args = [MSYS2_MINGW64_EXECUTABLE, 'cmake'] + else: + cmake_args = ['cmake'] + + process = subprocess.Popen(cmake_args + args_list, stdin=subprocess.PIPE, stderr=subprocess.PIPE, + stdout=subprocess.PIPE) + + logger.log(level=logging.INFO, msg=f"Running | cmake {' '.join(args_list)} | with PID: {process.pid}") + + if os.name == 'nt': + import psutil + + is_running = True + has_found_cmake = False + initial_timestamp = None + + while is_running: + + running_cmake = [] + for p in psutil.process_iter(): + try: + if p.name() == 'cmake.exe': + running_cmake.append(p) + has_found_cmake = True + except psutil.Error: + pass + + if not initial_timestamp: + initial_timestamp = datetime.now() + + if len(running_cmake) == 0 and has_found_cmake: + is_running = False + + time_elapsed_since_started_searching_for_cmake = datetime.now() - initial_timestamp + + if time_elapsed_since_started_searching_for_cmake.seconds > TIME_TO_WAIT_IF_IT_DIDNT_FIND_CMAKE_TASK and not has_found_cmake: + is_running = False + else: + process.wait() + + return process + + +class AuroraClient: + def __init__(self, env, port): + self.client = None + self.env = env + self.port = port + + def refresh(self): + self.compile() + self.kill() + self.run() + + def kill(self): + if self.client: + self.client.kill() + self.client.wait() + self.client = None + + def poll(self): + if self.client: + return self.client.poll() + return None + + def readline(self): + if self.client: + return self.client.stdout.readline() + return '' + + def compile(self): + current_working_dir = os.path.abspath(os.getcwd()) + + logger.log(level=logging.INFO, msg=f"Starting compilation of the client | ENV: {self.env}") + build_dir = f"{CLIENT_DIR}/build-{self.env}" + + build_path = Path(build_dir) + if not build_path.exists(): + os.mkdir(build_dir) + + extra_build_args = [] + + if os.name == 'nt': + extra_build_args = ['-G', "MinGW Makefiles"] + + os.chdir(build_dir) + + run_cmake_command(extra_build_args + ['..', f"-DCMAKE_BUILD_TYPE={self.env}"]) + run_cmake_command(['--build', "."]) + run_cmake_command(['--install', "."]) + + os.chdir(current_working_dir) + + logger.log(level=logging.INFO, msg=f"Finished compilation of the client!") + + def run(self): + current_working_dir = os.path.abspath(os.getcwd()) + + os.chdir(CLIENT_DIR) + + executable = f"./bin/{self.env}/Aurora" + client = subprocess.Popen([executable, str(self.port)], stdout=subprocess.PIPE, universal_newlines=True) + + os.chdir(current_working_dir) + + self.client = client + return client diff --git a/launcher/Helpers/constants.py b/launcher/Helpers/constants.py new file mode 100644 index 0000000..573bbd1 --- /dev/null +++ b/launcher/Helpers/constants.py @@ -0,0 +1,45 @@ +import sys + +CLIENT_DIR="client" +SERVER_DIR="server" + +MSYS2_MINGW64_EXECUTABLE = "C:\\msys64\\mingw64.exe" # windows (msys2) specific +TIME_TO_WAIT_IF_IT_DIDNT_FIND_CMAKE_TASK = 0.2 # in seconds / windows (msys2) specific + +TIME_TO_ELAPSE_FROM_LAST_CLIENT_COMPILATION = 5 # in seconds +TIME_TO_ELAPSE_FROM_LAST_SERVER_COMPILATION = 5 # in seconds + +DIRECTORIES_TO_WATCH_FOR_CLIENT_RECOMPILATION = ["src\\", "include\\", "src/", "include/"] + +arguments_default_values = { + "port": 8000, + "env": 'debug', + 'verbose': False +} + + +def get_arg_index(arg_name): + index = -1 + + for i, arg in enumerate(sys.argv): + mini_arg_str = f"-{arg_name[0]}" + full_arg_str = f"--{arg_name}" + + if arg == mini_arg_str or arg == full_arg_str: + index = i + continue + + return index + + +def get_arg_value(arg_name): + arg_index = get_arg_index(arg_name) + + if arg_index == -1: + return arguments_default_values[arg_name] + + return sys.argv[arg_index+1] + + +def get_arg_exist(arg_name): + return get_arg_index(arg_name) != -1 \ No newline at end of file diff --git a/launcher/Helpers/event_handlers.py b/launcher/Helpers/event_handlers.py new file mode 100644 index 0000000..f89a4bc --- /dev/null +++ b/launcher/Helpers/event_handlers.py @@ -0,0 +1,49 @@ +from watchdog.events import FileSystemEventHandler +from datetime import datetime +from Helpers.constants import \ + TIME_TO_ELAPSE_FROM_LAST_CLIENT_COMPILATION, \ + TIME_TO_ELAPSE_FROM_LAST_SERVER_COMPILATION, \ + DIRECTORIES_TO_WATCH_FOR_CLIENT_RECOMPILATION + + +class ClientEventHandler(FileSystemEventHandler): + modified_files = [] + + def __init__(self, client): + super().__init__() + self.has_compilation_started = False + self.last_compilation_at = datetime.now() + self.client = client + + def on_modified(self, event): + super().on_modified(event) + + seconds_since_last_compilation = (datetime.now() - self.last_compilation_at).seconds + + if not event.is_directory: + for dir_to_watch in DIRECTORIES_TO_WATCH_FOR_CLIENT_RECOMPILATION: + if event.src_path.find( + dir_to_watch) != -1 and seconds_since_last_compilation >= TIME_TO_ELAPSE_FROM_LAST_CLIENT_COMPILATION: + # If the changed file is inside the directories we're watching for, refresh the client + self.client.refresh() + self.last_compilation_at = datetime.now() + + +class ServerEventHandler(FileSystemEventHandler): + modified_files = [] + + def __init__(self, server): + super().__init__() + self.last_ran_at = datetime.now() + self.server = server + + def on_modified(self, event): + super().on_modified(event) + + seconds_since_last_run = (datetime.now() - self.last_ran_at).seconds + + if not event.is_directory: + if seconds_since_last_run >= TIME_TO_ELAPSE_FROM_LAST_SERVER_COMPILATION: + # If the changed file is inside the directories we're watching for, refresh the client + self.server.refresh() + self.last_ran_at = datetime.now() \ No newline at end of file diff --git a/launcher/Helpers/logger.py b/launcher/Helpers/logger.py new file mode 100644 index 0000000..cecd045 --- /dev/null +++ b/launcher/Helpers/logger.py @@ -0,0 +1,41 @@ +import logging +import logging.handlers +from datetime import datetime +import os + +from Helpers.root_dir import ROOT_DIR +from pathlib import Path + +existing_loggers = {}; + +def make_logger(name): + + if name in existing_loggers: + return existing_loggers[name] + + now = datetime.now() + now_str = now.strftime("%d_%m_%Y_%H_%M_%S") + + formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") + + Path(f"{ROOT_DIR}/Logs").mkdir(parents=True, exist_ok=True) + + file_handler = logging.handlers.WatchedFileHandler( + os.environ.get("LOGFILE", f"{ROOT_DIR}/Logs/aurora_launcher_{now_str}.log")) + file_handler.setFormatter(formatter) + + console_handler = logging.StreamHandler() + console_handler.setFormatter(formatter) + + logger = logging.getLogger(name) + logger.setLevel(os.environ.get("LOGLEVEL", "INFO")) + + for handler in logger.handlers: + logger.removeHandler(handler) + + logger.addHandler(file_handler) + logger.addHandler(console_handler) + + existing_loggers[name] = logger + + return logger \ No newline at end of file diff --git a/launcher/Helpers/root_dir.py b/launcher/Helpers/root_dir.py new file mode 100644 index 0000000..3ea120c --- /dev/null +++ b/launcher/Helpers/root_dir.py @@ -0,0 +1,2 @@ +import os +ROOT_DIR = f"{os.path.dirname(os.path.abspath(__file__))}/.." \ No newline at end of file diff --git a/launcher/Helpers/server.py b/launcher/Helpers/server.py new file mode 100644 index 0000000..142321b --- /dev/null +++ b/launcher/Helpers/server.py @@ -0,0 +1,39 @@ +import subprocess +import os + +# Constants +from Helpers.constants import SERVER_DIR +from Helpers.logger import make_logger + +logger = make_logger('Launcher') +SERVER_DIR = "server" + + +class AuroraServer: + def __init__(self, env, port, should_log): + self.server = None + self.env = env + self.port = port + self.should_log = should_log + + def refresh(self): + self.kill() + self.run() + + def kill(self): + if self.server: + self.server.kill() + self.server.wait() + + def run(self): + current_working_dir = os.path.abspath(os.getcwd()) + + os.chdir(SERVER_DIR) + + server = subprocess.Popen(["python3", "main.py", str(self.port), '-v' if self.should_log else None]) + + os.chdir(current_working_dir) + + self.server = server + logger.info("Starting up server!") + return server diff --git a/launcher/development.py b/launcher/development.py new file mode 100644 index 0000000..61ccd78 --- /dev/null +++ b/launcher/development.py @@ -0,0 +1,53 @@ +import time +import os +import pathlib +from watchdog.observers import Observer + +from Helpers.client import AuroraClient +from Helpers.server import AuroraServer +from Helpers.constants import CLIENT_DIR, SERVER_DIR, get_arg_exist, get_arg_value +from Helpers.event_handlers import ClientEventHandler +from Helpers.logger import make_logger + +client_logger = make_logger('Client ') +launcher_logger = make_logger('Launcher') +should_log = get_arg_exist('verbose') + +file_path = pathlib.Path(__file__).parent.resolve() +os.chdir(f"{file_path}/../") + +env = get_arg_value('env') +port = get_arg_value('port') + +server = AuroraServer(env, port, should_log) +client = AuroraClient(env, port) + +if __name__ == "__main__": + server.refresh() + client.refresh() + + server_event_handler = ClientEventHandler(server) + server_observer = Observer() + server_observer.schedule(server_event_handler, f"./{SERVER_DIR}", recursive=True) + server_observer.start() + + client_event_handler = ClientEventHandler(client) + client_observer = Observer() + client_observer.schedule(client_event_handler, f"./{CLIENT_DIR}", recursive=True) + client_observer.start() + + try: + while True: + output = client.readline() + if output == '' and client.poll() is not None: + break + if output and should_log: + client_logger.info(output.strip()) + except KeyboardInterrupt: + launcher_logger.info("Shutting down gracefully...") + client.kill() + server.kill() + client_observer.stop() + server_observer.stop() + client_observer.join() + server_observer.join() diff --git a/launcher/requirements.txt b/launcher/requirements.txt new file mode 100644 index 0000000..6cb4d64 --- /dev/null +++ b/launcher/requirements.txt @@ -0,0 +1,2 @@ +watchdog +psutil \ No newline at end of file diff --git a/server/.gitignore b/server/.gitignore index 48e0f15..1914a42 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -1,4 +1,4 @@ -Logs/*.log +*.log # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/server/Integrations/steam.py b/server/Integrations/steam.py index 33c3433..9441998 100644 --- a/server/Integrations/steam.py +++ b/server/Integrations/steam.py @@ -12,7 +12,12 @@ def get_all_steam_libraries_fs(): steam_libraries = [f"{steam_base_dir}/steamapps/"] extra_steam_libraries_vdf = vdf.load(open(f"{steam_base_dir}/steamapps/libraryfolders.vdf")) - for key, extra_steam_library in extra_steam_libraries_vdf['LibraryFolders'].items(): + try: + library_folders = extra_steam_libraries_vdf['LibraryFolders'] + except: + library_folders = extra_steam_libraries_vdf['libraryfolders'] + + for key, extra_steam_library in library_folders.items(): if key.isnumeric(): # Steam libraries inside the vdf will have numeric only keys steam_libraries.append(f"{extra_steam_library}/steamapps/") diff --git a/server/Utilities/constants/steam_constants.py b/server/Utilities/constants/steam_constants.py index 337e362..6b2bd52 100644 --- a/server/Utilities/constants/steam_constants.py +++ b/server/Utilities/constants/steam_constants.py @@ -1,7 +1,10 @@ +import os + steam_constants = { "base_dir": { "win32": "C:/Program Files (x86)/Steam", - "linux": "~/.local/share/steam", + "linux": os.path.expanduser("~/.local/share/steam"), + "darwin": os.path.expanduser("~/Library/Application Support/Steam") }, "platform_name": "steam", "blacklisted_game_ids": [228980] diff --git a/server/Utilities/logger.py b/server/Utilities/logger.py index ddbc9dd..91ad843 100644 --- a/server/Utilities/logger.py +++ b/server/Utilities/logger.py @@ -4,11 +4,14 @@ import os from Utilities.root_dir import ROOT_DIR +from pathlib import Path now = datetime.now() now_str = now.strftime("%d_%m_%Y_%H_%M_%S") -formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") + +Path(f"{ROOT_DIR}/Logs").mkdir(parents=True, exist_ok=True) file_handler = logging.handlers.WatchedFileHandler( os.environ.get("LOGFILE", f"{ROOT_DIR}/Logs/aurora_server_{now_str}.log")) @@ -17,7 +20,7 @@ console_handler = logging.StreamHandler() console_handler.setFormatter(formatter) -logger = logging.getLogger("Aurora") +logger = logging.getLogger("Server") logger.setLevel(os.environ.get("LOGLEVEL", "INFO")) for handler in logger.handlers: diff --git a/server/main.py b/server/main.py index 8d8c21b..2679c44 100644 --- a/server/main.py +++ b/server/main.py @@ -1,8 +1,22 @@ from Integrations.steam import get_all_steam_games_from_filesystem -from wsgiref.simple_server import make_server +from wsgiref.simple_server import WSGIRequestHandler, make_server +from Utilities.logger import logger import falcon +import sys +should_log = False +if len(sys.argv) >= 3 and sys.argv[2] == '-v': + should_log = True + +if not should_log: + logger.disabled = True + + +class NoLoggingWSGIRequestHandler(WSGIRequestHandler): + + def log_message(self, format, *args): + pass class SteamGamesResource: def on_get(self, req, resp): @@ -19,8 +33,11 @@ def on_get(self, req, resp): app.add_route('/steam', steam) if __name__ == '__main__': - with make_server('', 8000, app) as httpd: - print('Serving on port 8000...') + with make_server('', int(sys.argv[1]), app, handler_class=NoLoggingWSGIRequestHandler) as httpd: + logger.info(f'Serving on port {sys.argv[1]}...') # Serve until process is killed - httpd.serve_forever() \ No newline at end of file + try: + httpd.serve_forever() + except: + httpd.server_close() \ No newline at end of file