├── README.md ├── roblox-linux-launcher.py └── runRoblox.py /README.md: -------------------------------------------------------------------------------- 1 | # Roblox-Linux-Launcher 2 | 3 | Launch Roblox on Linux! 4 | 5 | (Everything is still in development. Consider nothing to be complete.) 6 | 7 | With the release of Wine 6.11, it is now possible to play Roblox on Linux. Roblox Linux Launcher has been rewritten from the ground up to quickly and easily launch your Roblox games. 8 | 9 | ## Features: 10 | - Launch Roblox games quickly and easily 11 | - All browsers supported through xdg-open 12 | - Automatically detects new Roblox versions and adjusts the version to launch accordingly* 13 | 14 | *Ensure you only have one version of Roblox installed before using Roblox Linux Launcher. I suggest making a fresh install of Roblox before using this program. 15 | 16 | ## Soon: 17 | 18 | - Compiled version (no need to install python packages with `pip`, will use GUI for messages instead of terminal, and will include .desktop launcher) 19 | 20 | ## Dependencies: 21 | 22 | - xdg-open from xdg-utils 23 | - Wine 6.11 or greater (currently development branch) 24 | - Roblox installation **with only one version installed (see above)** 25 | - Python 3 26 | 27 | ## What about other launchers? 28 | Of course, there are other launchers available to launch Roblox with. It really just comes down to personal preference. At the end of the day, a launcher is a launcher. 29 | 30 | lol I'm just a fello trying to play [Phantom Forces](https://www.roblox.com/games/292439477/Phantom-Forces) on linux 31 | -------------------------------------------------------------------------------- /roblox-linux-launcher.py: -------------------------------------------------------------------------------- 1 | #Roblox Linux Launcher - play Roblox on Linux! 2 | #You will need Google Chrome or Brave for this to work. Because we need to log console output using a launch flag, and we can use this to pull our Roblox Launch Arugment. 3 | #There is no need to sign into Chrome if you don't want to. 4 | #After selecting a game, the browser will close. Logging will not be enabled for future general Chrome sessions - it only gets enabled in Roblox Linux Launcher. 5 | import os 6 | import time 7 | import threading 8 | import logging 9 | import logging.config 10 | import json 11 | import subprocess 12 | import sys 13 | from sys import version_info 14 | from sys import argv 15 | from distutils import spawn 16 | 17 | #Configure logging. 18 | current_module = sys.modules[__name__] 19 | moduledir=os.path.dirname(current_module.__file__) 20 | logfile=os.path.join(moduledir, "robloxLauncher.log") 21 | configLogfileName=os.path.join(moduledir, "log.cfg") 22 | 23 | if (os.path.exists(configLogfileName)): 24 | logConfigFile = open(configLogfileName) 25 | config = json.load(logConfigFile) 26 | logging.config.dictConfig(config) 27 | else: 28 | logging.basicConfig(level=logging.DEBUG, format="%(levelname)s %(message)s") 29 | 30 | #Log all unhandled exceptions 31 | def handle_unhandled_exception(exc_type, exc_value, exc_traceback): 32 | """Handler for unhandled exceptions that will write to the logs""" 33 | if issubclass(exc_type, KeyboardInterrupt): 34 | sys.__excepthook__(exc_type, exc_value, exc_traceback) 35 | return 36 | logging.critical("Unhandled exception", exc_info=(exc_type, exc_value, exc_traceback)) 37 | 38 | sys.excepthook = handle_unhandled_exception 39 | logging.info("Starting roblox launcher: " + " ".join(argv)) 40 | 41 | class Browser: 42 | def __init__(self, p, exe, l, i): 43 | self.pgrep_pattern = p 44 | self.browser_exe = exe 45 | self.log_location = l 46 | self.invocation = i 47 | def __str__(self): 48 | return self.pgrep_pattern 49 | 50 | BRAVE = Browser("brave", "brave-browser", "~/.config/BraveSoftware/Brave-Browser/chrome_debug.log", 51 | 'brave-browser --app --window-size=1280,720 --enable-logging https://www.roblox.com' 52 | ) 53 | CHROME = Browser("chrome", "google-chrome-stable", "~/.config/google-chrome/chrome_debug.log", 54 | 'google-chrome-stable --app=https://www.roblox.com --window-size=1280,720 --enable-logging' 55 | ) 56 | CHROMIUM = Browser("chromium", "chromium", "~/.config/chromium/chrome_debug.log", 57 | 'chromium --app=https://www.roblox.com --window-size=1280,720 --enable-logging' 58 | ) 59 | 60 | browser_list = [BRAVE, CHROME, CHROMIUM] 61 | launcher_name = argv[0] 62 | 63 | def launcher_help(): 64 | print() 65 | print("All browses are supported when " + launcher_name + "\n" 66 | "is configured for xdg-open. xdg-open associates the url \n" 67 | "prefix 'roblox-player:' with an application to handle the url.") 68 | print("Install, test then in your browser select the roblox game play button.") 69 | print() 70 | print("Install xdg-open config: $ " + launcher_name + " install ") 71 | print("Test xdg-open : $ " + launcher_name + " test ") 72 | print("Uninstall xdg-open config: $ " + launcher_name + " uninstall ") 73 | print() 74 | print("Enable logging to file for debugging: $ " + launcher_name + " logon ") 75 | print("Captures and stores messages for launcher xdg-open invocation.") 76 | print("Disable by removing the log.cfg file.") 77 | print() 78 | print("Valid browses for invocation without configuring xdg-open: " + 79 | str([str(webb) for webb in browser_list])) 80 | print("Execute examples:") 81 | print("Execute the default browser: $ "+ launcher_name) 82 | print("Execute the brave browser: $ "+ launcher_name + " " 83 | + BRAVE.pgrep_pattern) 84 | 85 | #Configure logging to file for debug. Delete the file to disable. 86 | def logon(): 87 | LOGGING_CONFIG=\ 88 | { 89 | "version": 1, 90 | "disable_existing_loggers": "true", 91 | "formatters": { 92 | "simple": { 93 | "format": "%(asctime)s %(levelname)s %(filename)s %(message)s", 94 | "datefmt": "%Y-%m-%d %H:%M:%S" 95 | }, 96 | "console": { 97 | "format": "%(levelname)s %(message)s" 98 | } 99 | }, 100 | "handlers": { 101 | "file_handler": { 102 | "level": "INFO", 103 | "class": "logging.handlers.RotatingFileHandler", 104 | "formatter": "simple", 105 | "filename": logfile, 106 | "backupCount": 1, 107 | "maxBytes": 2048, 108 | "mode": "a", 109 | "encoding": "utf8" 110 | }, 111 | "console": { 112 | "level": "INFO", 113 | "class": "logging.StreamHandler", 114 | "formatter": "console" 115 | } 116 | }, 117 | "loggers": {}, 118 | "root": { 119 | "handlers": [ 120 | "file_handler", 121 | "console" 122 | ], 123 | "level": "DEBUG" 124 | } 125 | } 126 | configFile=open(configLogfileName,'w') 127 | configFile.write(json.dumps(LOGGING_CONFIG, indent=4)) 128 | configFile.close() 129 | 130 | #Create the xdg-open .desktop file and install or uninstall. 131 | def setDesktopFile(filetype): 132 | desktopFile=os.path.join(moduledir, "robloxlinux-launcher.desktop") 133 | if (filetype == "install"): 134 | desktopData="[Desktop Entry]\nVersion=1.0\n" \ 135 | + "Type=Application\nName=Roblox Player\nNoDisplay=true\n" \ 136 | + "Comment=Roblox Game Launcher\n" \ 137 | + "MimeType=x-scheme-handler/roblox-player;\nCategories=Game;\n" \ 138 | + "Exec=/opt/game/Roblox-Linux-Launcher/runRoblox.py\n" 139 | print("Installing " + desktopFile) 140 | with open(desktopFile, 'w') as file: 141 | file.write(desktopData) 142 | os.system("xdg-desktop-menu install " + desktopFile) 143 | os.system("xdg-mime default " \ 144 | + "robloxlinux-launcher.desktop x-scheme-handler/roblox-player") 145 | else: 146 | #uninstall 147 | if os.path.exists(desktopFile): 148 | os.system("xdg-desktop-menu uninstall " + desktopFile) 149 | os.remove(desktopFile) 150 | else: 151 | print("The desktop file is not installed.") 152 | 153 | browser = None 154 | robloxData = None 155 | 156 | home = os.getenv("HOME") 157 | rll_version="1.0-alpha" 158 | wineprefix = os.getenv("WINEPREFIX",f"{home}/.wine") 159 | if os.path.isdir(wineprefix) == False: 160 | logging.critical("WINE does not exist: " + wineprefix) 161 | quit() 162 | 163 | versions_folder = f"{wineprefix}/drive_c/Program Files (x86)/Roblox/Versions" 164 | if os.path.isdir(versions_folder) == False: 165 | logging.critical("Roblox is not installed: " + versions_folder) 166 | quit() 167 | 168 | def getRobloxVersion(): #Roblox version strings seem to be random. To check if Roblox has updated, we see if any new directories have been created. If they have, make that the current directory. 169 | logging.info("Checking Roblox version.") 170 | if os.path.isfile(f"{home}/roblox-linux-launcher/versions.txt") == False: 171 | logging.info("versions.txt doesn't exist. Generating...") 172 | os.mkdir(f"{home}/roblox-linux-launcher") 173 | newFile = open(f"{home}/roblox-linux-launcher/versions.txt","w") 174 | newFile.close() 175 | if os.path.isfile(f"{home}/roblox-linux-launcher/current_version.txt") == False: 176 | logging.info("current_version.txt doesn't exist. Generating...") 177 | newFile = open(f"{home}/roblox-linux-launcher/current_version.txt","w") 178 | newFile.close() 179 | versions_folder_entries = (os.listdir(versions_folder)) 180 | all_versions = open(f"{home}/roblox-linux-launcher/versions.txt","r") 181 | all_text = all_versions.read() 182 | all_versions.close() 183 | 184 | for i in range(len(versions_folder_entries)): 185 | 186 | 187 | if os.path.isdir(f"{versions_folder}/{versions_folder_entries[i]}") == True: 188 | if f"{versions_folder}/{versions_folder_entries[i]}" not in all_text: 189 | logging.info(f"New Version Found: {versions_folder_entries[i]}") 190 | all_versions = open(f"{home}/roblox-linux-launcher/versions.txt","a") 191 | all_versions.write(f"{versions_folder}/{versions_folder_entries[i]}\n") 192 | all_versions.close() 193 | current_version = open(f"{home}/roblox-linux-launcher/current_version.txt","w") 194 | current_version.write(f"{versions_folder}/{versions_folder_entries[i]}") 195 | current_version.close() 196 | logging.info(f"{versions_folder_entries[i]} is now the current version.") 197 | 198 | def openRobloxSite(): 199 | chromeCheck = os.system(f"pgrep {browser.pgrep_pattern}") 200 | if chromeCheck != 256: 201 | print('Web browser is already running. It must be restarted to work with Roblox Linux Launcher. Restart the browser? (y/N)') 202 | choice = input('>') 203 | if choice.lower() == "y": 204 | os.system(f"pkill {browser.pgrep_pattern}") 205 | else: 206 | exit() 207 | checkFunction = threading.Thread(target=checkForLaunchArg) 208 | checkFunction.start() 209 | os.system(browser.invocation) 210 | checkFunction.join() 211 | 212 | def checkForLaunchArg(): #Check for a launch argument every 1.5 seconds. 213 | haveLaunchArg = False 214 | while haveLaunchArg == False: 215 | time.sleep(1.5) 216 | log_grep = 'grep -i "roblox-player:" ' + browser.log_location 217 | launcharg = os.popen(log_grep).read() 218 | if launcharg != '': 219 | haveLaunchArg = True 220 | os.system(f"pkill {browser.pgrep_pattern}") 221 | launchGame(launcharg) 222 | 223 | def launchGame(launcharg): 224 | current_version = open(f"{home}/roblox-linux-launcher/current_version.txt","r") 225 | current_version_text = current_version.read() 226 | current_version.close() 227 | trimLaunchArgStart = launcharg.find('roblox-player') 228 | trimLaunchArgEnd = launcharg.find('channel:') 229 | launcharg = launcharg[trimLaunchArgStart:(trimLaunchArgEnd+8)] 230 | logging.info(f"Launch Arg: {launcharg}") 231 | logging.info(f"Current Version Text: {current_version_text}") 232 | os.system(f'wine "{current_version_text}/RobloxPlayerLauncher.exe" {launcharg}') 233 | 234 | if len(argv) == 2: 235 | robloxArg = argv[1] 236 | if robloxArg.startswith("roblox-player:"): 237 | #either a roblox game or our xdg-open test 238 | robloxData=robloxArg 239 | elif robloxArg == "install" or robloxArg == "uninstall": 240 | #turn on xdg output 241 | os.environ["XDG_UTILS_DEBUG_LEVEL"] = "2" 242 | #Install mimetype handler config ie .desktop file 243 | if robloxArg == "install": 244 | setDesktopFile("install") 245 | else: 246 | setDesktopFile("uninstall") 247 | os.environ["XDG_UTILS_DEBUG_LEVEL"] = "0" 248 | quit() 249 | elif robloxArg == "logon": 250 | #setup logging output log file. 251 | logon() 252 | quit() 253 | elif robloxArg == "test": 254 | #test xdg-open operation for roblox url. 255 | os.environ["XDG_UTILS_DEBUG_LEVEL"] = "2" 256 | cmd = "xdg-open roblox-player:test" 257 | logging.info("Run: " + cmd) 258 | os.system(cmd) 259 | logging.info("Test end. (previous message should say success.)") 260 | os.environ["XDG_UTILS_DEBUG_LEVEL"] = "0" 261 | quit() 262 | else: 263 | web_browser_type = argv[1] 264 | for web_browser in browser_list: 265 | if web_browser.pgrep_pattern == web_browser_type: 266 | browser = web_browser; 267 | break; 268 | if browser is None: 269 | print("Invalid browser selected: " + web_browser_type) 270 | launcher_help() 271 | quit() 272 | else: 273 | browser = CHROME 274 | 275 | print(f"Roblox Linux Launcher {rll_version}") 276 | print('IMPORTANT:When prompted to open with "xdg-open" select "Open". We suggest checking the box to enable this by default.') 277 | getRobloxVersion() 278 | 279 | if robloxData is not None: 280 | if robloxArg.startswith("roblox-player:test"): 281 | logging.info("xdg-open test success! You do not need to run this script again,\n" 282 | + " xdg-open will execute it from your browser.\n" 283 | + " Just press play for the roblox game in your browser.") 284 | else: 285 | logging.info("Launching roblox. Please wait.") 286 | launchGame(robloxData) 287 | else: 288 | if (spawn.find_executable(browser.browser_exe)): 289 | #Check if robloxlinux-launcher.desktop is installed. 290 | launcherfile=os.path.join(moduledir, "robloxlinux-launcher.desktop") 291 | if (os.path.exists(configLogfileName)): 292 | logging.error("Configuration is for xdg-open. No need to execute " + launcher_name + ",\n" 293 | + "just press the play button for the game on the roblox site.\n" 294 | + "Else uninstall the xdg config: " + launcher_name + " uninstall") 295 | quit() 296 | #open browser, check browser log for roblox data, launch roblox. 297 | logging.info("Opening roblox site.") 298 | openRobloxSite() 299 | else: 300 | logging.error("Executable does not exist: "+browser.browser_exe); 301 | launcher_help() 302 | 303 | # vim: tabstop=4 shiftwidth=4 expandtab: 304 | -------------------------------------------------------------------------------- /runRoblox.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys; 4 | 5 | if sys.version_info.major < 3: 6 | print("Require python version 3") 7 | quit() 8 | 9 | __import__("roblox-linux-launcher") 10 | --------------------------------------------------------------------------------