def connectSocket(self): if not self.connected: # print("Connecting...") self.socket.connect( ("127.0.0.1", int(self.DS.settings.ConsolePort))) self.connected = True AstroLogging.logPrint("Connected to RCON Console!")
def heartbeat_server(serverData, headers, dataToChange=None): try: url = ( "https://5EA1.playfabapi.com/Client/ExecuteCloudScript?sdk=UE4MKPL-1.19.190610" ) requestObj = { "FunctionName": "heartbeatDedicatedServer", "FunctionParameter": { "serverName": serverData['Tags']['serverName'], "buildVersion": serverData['Tags']['gameBuild'], "gameMode": serverData['GameMode'], "ipAddress": serverData['ServerIPV4Address'], "port": serverData['ServerPort'], "matchmakerBuild": serverData['BuildVersion'], "maxPlayers": serverData['Tags']['maxPlayers'], "numPlayers": str(len(serverData['PlayerUserIds'])), "lobbyId": serverData['LobbyID'], "publicSigningKey": serverData['Tags']['publicSigningKey'], "requiresPassword": serverData['Tags']['requiresPassword'] }, "GeneratePlayStreamEvent": True } if dataToChange is not None: requestObj['FunctionParameter'].update(dataToChange) AstroLogging.logPrint(requestObj, "debug") x = (requests.post(url, headers=headers, json=requestObj)).json() AstroLogging.logPrint(x, "debug") return x except: return {"status": "Error"}
def saveGame(self): self.setStatus("saving") self.busy = True time.sleep(1) AstroLogging.logPrint("Saving the current game...") AstroRCON.DSSaveGame(self.settings.ConsolePort) self.busy = False
def kill_server(self, reason, save=False): AstroLogging.logPrint(f"Kill Server: {reason}") try: self.busy = True self.setStatus("shutdown") except: pass try: if save: self.saveGame() time.sleep(1) self.shutdownServer() except: pass try: self.deregister_all_server() except: pass # Kill all child processes try: for child in psutil.Process(self.process.pid).children(): child.kill() except: pass try: self.setStatus("off") except: pass # Kill current process try: os.kill(os.getpid(), 9) except: pass
def on_modified(self, event): # AstroLogging.logPrint("File in save directory changed") path = os.path.join(self.astroPath, self.moveToPath) try: if not os.path.exists(path): os.makedirs(path) except Exception as e: AstroLogging.logPrint(e, "error") now = time.time() try: for f in os.listdir(path): fpath = os.path.join(path, f) if os.stat(fpath).st_mtime < ( now - (self.retentionPeriodHours * 60 * 60)): os.remove(fpath) except Exception as e: AstroLogging.logPrint(e, "error") AstroLogging.logPrint("Copying backup to retention folder.") time.sleep(1) try: dirName = os.path.dirname(event.src_path) newFile = os.path.join(dirName, [ f for f in os.listdir(dirName) if os.path.isfile(os.path.join(dirName, f)) ][0]) # AstroLogging.logPrint(newFile, "debug") shutil.copy2(newFile, path) # AstroLogging.logPrint(copiedFile, "debug") except FileNotFoundError as e: AstroLogging.logPrint(e, "error") except Exception as e: AstroLogging.logPrint(e, "error") self.launcher.backupObserver.stop()
def __init__(self, container_file_path): """ Extracts the saves from an Astroneer save container Reads the container file, divides it into chunks and regroups the chunks into save objects Arguments: container_file_path -- Path to the container file Returns: The AstroSaveContainer object Exception: None """ self.path = container_file_path self.save_list = [] AstroLogging.logPrint('\nInitializing Astroneer save container...') with open(self.path, "rb") as container: # The Astroneer file type is contained in at least the first 2 bytes of the file self.header = container.read(2) if not self.is_valid_container_header(self.header): raise Exception( f'The save container {self.path} is not valid (First two bytes:{self.header})') # Skipping 2 bytes that may be part of the header container.read(2) # Next 4 bytes are the number of saves chunk self.chunk_count = int.from_bytes( container.read(4), byteorder='little') AstroLogging.logPrint(f'Detected chunks: {self.chunk_count}') # Parsing saves chunks current_save_name = None for _ in range(self.chunk_count): current_chunk = container.read(CHUNK_METADATA_SIZE) current_chunk_name = self.extract_name_from_chunk( current_chunk) if current_chunk_name != current_save_name: if current_save_name != None: # Parsing a new save, storing the current save self.save_list.append(AstroSave(current_save_name, current_chunks_names)) current_chunks_names = [] current_save_name = current_chunk_name current_chunks_names.append( self.extract_chunk_file_name_from_chunk(current_chunk)) # Saving the last save of the container file self.save_list.append(AstroSave(current_save_name, current_chunks_names))
def on_modified(self, event): time.sleep(1) dirName = os.path.dirname(event.src_path) fileName = [ f for f in os.listdir(dirName) if os.path.isfile(os.path.join(dirName, f)) ][0] AstroLogging.logPrint(f"Server saved. {fileName}") self.launcher.saveObserver.stop()
def get_microsoft_save_folder(): """ Retrieves the microsoft save folders from %appdata% We know that the saves are stored along with a container.* file. We look for that specific container by checking if it contains a save date in order to return the whole path Arguments: None Returns: Returns the list of the microsoft save folder path found in %appdata% Exception: FileNotFoundError if no save folder is found """ # glob is used to get the path using the wildcard * for result in glob.iglob( os.environ['LOCALAPPDATA'] + '\\Packages\\SystemEraSoftworks*\\SystemAppData\\wgs'): AstroLogging.logPrint(f'SES path found in appadata: {result}', 'debug') SES_appdata_path = result microsoft_save_folders = [] for root, _, files in os.walk(SES_appdata_path): # Seeking every file in the SES folder for file in files: if re.search(r'^container\.', file): # We matched a container.* file container_full_path = os.path.join(root, file) AstroLogging.logPrint( f'Container file found:{container_full_path}', 'debug') with open(container_full_path, 'rb') as container: # Decoding the container to check for a date string container_binary_content = container.read() container_text = container_binary_content.decode( 'utf-16le', errors='ignore') # Save date matches $YYYY.MM.dd if re.search(r'\$\d{4}\.\d{2}\.\d{2}', container_text): AstroLogging.logPrint(f'Matching save folder {root}', 'debug') microsoft_save_folders.append(root) if not microsoft_save_folders: AstroLogging.logPrint(f'No save folder found.', 'debug') raise FileNotFoundError elif len(microsoft_save_folders) != 1: # We are not supposed to have more than one save folder AstroLogging.logPrint( f'More than one save folders was found:\n {microsoft_save_folders}', 'debug') return microsoft_save_folders
def shutdownServer(self): if not self.AstroRCON.connected: return False self.setStatus("shutdown") self.busy = True # time.sleep(1) self.AstroRCON.DSServerShutdown() self.DSServerStats = None AstroLogging.logPrint("Server shutdown.")
def saveGame(self): if not self.AstroRCON.connected: return False self.setStatus("saving") self.busy = True # time.sleep(1) AstroLogging.logPrint("Saving the current game...") self.AstroRCON.DSSaveGame() self.busy = False
def saveGame(self, name=None): if not self.AstroRCON.connected: return False self.setStatus("saving") self.busy = "Saving" # time.sleep(1) AstroLogging.logPrint("Saving the current game...") self.AstroRCON.DSSaveGame(name) self.getSaves() self.busy = False
def start_server(self): """ Starts the Dedicated Server process and waits for it to be registered """ self.DedicatedServer.status = "starting" self.DedicatedServer.busy = False oldLobbyIDs = self.DedicatedServer.deregister_all_server() AstroLogging.logPrint("Starting Server process...") if self.launcherConfig.EnableAutoRestart: AstroLogging.logPrint( f"Next restart is at {self.DedicatedServer.nextRestartTime}") time.sleep(5) startTime = time.time() self.DedicatedServer.start() self.DaemonProcess = AstroDaemon.launch( executable=self.isExecutable, consolePID=self.DedicatedServer.process.pid) # Wait for server to finish registering... while not self.DedicatedServer.registered: try: serverData = (AstroAPI.get_server( self.DedicatedServer.ipPortCombo, self.headers)) serverData = serverData['data']['Games'] lobbyIDs = [x['LobbyID'] for x in serverData] if len(set(lobbyIDs) - set(oldLobbyIDs)) == 0: time.sleep(self.launcherConfig.PlayfabAPIFrequency) else: now = time.time() if now - startTime > 15: self.DedicatedServer.registered = True del oldLobbyIDs self.DedicatedServer.LobbyID = serverData[0]['LobbyID'] if self.DedicatedServer.process.poll() is not None: AstroLogging.logPrint( "Server was forcefully closed before registration. Exiting...." ) return False except KeyboardInterrupt: self.DedicatedServer.kill_server("Launcher shutting down") except: AstroLogging.logPrint( "Failed to check server. Probably hit rate limit. Backing off and trying again..." ) self.launcherConfig.PlayfabAPIFrequency += 1 time.sleep(self.launcherConfig.PlayfabAPIFrequency) doneTime = time.time() elapsed = doneTime - startTime AstroLogging.logPrint( f"Server ready with ID {self.DedicatedServer.LobbyID}. Took {round(elapsed,2)} seconds to register." ) self.DedicatedServer.status = "ready" self.DedicatedServer.server_loop()
def generate_XAUTH(serverGUID): url = "https://5EA1.playfabapi.com/Client/LoginWithCustomID?sdk=UE4MKPL-1.19.190610" requestObj = { "CreateAccount": True, "CustomId": serverGUID, "TitleId": "5EA1" } AstroLogging.logPrint(requestObj, "debug") x = (requests.post(url, headers=base_headers, json=requestObj)).json() AstroLogging.logPrint(x, "debug") return x['data']['SessionTicket']
def newSaveGame(self): if not self.AstroRCON.connected: return False self.setStatus("newsave") self.busy = "NewSave" # time.sleep(1) AstroLogging.logPrint("Starting a new savegame...") self.AstroRCON.DSNewGame() self.AstroRCON.DSSaveGame() self.getSaves() self.busy = False
def get(self): # ip = self.request.headers.get("X-Real-IP") or \ # self.request.headers.get("X-Forwarded-For") or \ # self.request.remote_ip # if ip == "::1": evt = self.get_argument('evt') msg = self.get_argument('msg') name = self.get_argument('name') if evt == "chat" or evt == "cmd": AstroLogging.logPrint(msg, msgType=evt, playerName=name, dwet="c") self.write({"message": "Success"})
def validate_playfab_certs(self): AstroLogging.logPrint("Attempting to validate Playfab Certs") playfabRequestCommand = [ "powershell", '-executionpolicy', 'bypass', '-command', 'Invoke-WebRequest -uri https://5ea1.playfabapi.com/ -UseBasicParsing' ] with open(os.devnull, 'w') as tempf: proc = subprocess.Popen(playfabRequestCommand, stdout=tempf, stderr=tempf) proc.communicate()
def connectSocket(self): if not self.connected: # print("Connecting...") self.socket.connect( ("127.0.0.1", int(self.DS.settings.ConsolePort))) with self.lockRcon() as s: # Send console password s.socket.sendall( f"{self.DS.settings.ConsolePassword}\n".encode()) # print(f"{self.DS.settings.ConsolePassword}\n".encode()) self.connected = True AstroLogging.logPrint("Connected to RCON Console!")
def loadSaveGame(self, saveData): if not self.AstroRCON.connected: return False self.setStatus("loadsave") self.busy = "LoadSave" name = saveData['name'] if pathvalidate.is_valid_filename(name): # time.sleep(1) AstroLogging.logPrint(f"Loading save: {name}") self.AstroRCON.DSLoadGame(name) self.getSaves() self.busy = False
def deregister_server(lobbyID, headers): url = 'https://5EA1.playfabapi.com/Client/ExecuteCloudScript?sdk=UE4MKPL-1.19.190610' requestObj = { "FunctionName": "deregisterDedicatedServer", "FunctionParameter": { "lobbyId": lobbyID }, "GeneratePlayStreamEvent": True } AstroLogging.logPrint(requestObj, "debug") x = (requests.post(url, headers=headers, json=requestObj)).json() AstroLogging.logPrint(x, "debug") return x
def get_container_list(path): """ List all containers in a folder Arguments: path -- path for search containers Returns: Returns a list of all containers found (only filename of container) Exception: None """ list_containers = [] for file in [ f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f)) ]: if file.rfind("container") != -1: list_containers.append(file) if not list_containers: AstroLogging.logPrint('\nNo container found in path: ' + path) elif len(list_containers) == 1: AstroLogging.logPrint('\nOne container found', 'debug') else: AstroLogging.logPrint('\nContainers found:') for i, container in enumerate(list_containers): AstroLogging.logPrint(f'\t {i+1}) {container}') return list_containers
def check_for_update(self): try: url = "https://api.github.com/repos/ricky-davis/AstroLauncher/releases/latest" data = ((requests.get(url)).json()) latestVersion = data['tag_name'] if latestVersion != self.version: AstroLogging.logPrint( f"UPDATE: There is a newer version of the launcher out! {latestVersion}" ) AstroLogging.logPrint(f"Download it at {self.latestURL}") if self.isExecutable and not self.launcherConfig.DisableAutoUpdate: self.autoupdate(data) except: pass
def deleteSaveGame(self, saveData): if not self.AstroRCON.connected: return False name = saveData['name'] if pathvalidate.is_valid_filename(name): self.setStatus("delsave") self.busy = "DelSave" saveGamePath = r"Astro\Saved\SaveGames" AstroLogging.logPrint(f"Deleting save: {saveData['fileName']}") sfPath = os.path.join(self.astroPath, saveGamePath, saveData['fileName']) if os.path.exists(sfPath): os.remove(sfPath) self.getSaves() self.busy = False
def __post_init__(self): # pylint: disable=no-member hasError = False for field, data in self.__dataclass_fields__.items(): try: self.__dict__[field] = data.type(self.__dict__[field]) except ValueError: hasError = True AstroLogging.logPrint( f"INI error: {field} must be of type {data.type.__name__}", "critical") if hasError: AstroLogging.logPrint("Fix your launcher config file!", "critical") sys.exit()
def __init__(self, launcher): logging.getLogger('tornado.access').disabled = True self.launcher = launcher self.port = self.launcher.launcherConfig.WebServerPort self.ssl = False curDir = self.launcher.launcherPath if self.launcher.isExecutable: curDir = sys._MEIPASS self.assetDir = os.path.join(curDir, "assets") # temp # these will later be saved and loaded from/to an .ini self.cookieSecret = secrets.token_hex(16).encode() self.passwordHash = self.launcher.launcherConfig.WebServerPasswordHash cfgOvr = {} if len(self.passwordHash) != 64: AstroLogging.logPrint( "SECURITY ALERT: You must set your Web Server Password!", "warning") cfgOvr["WebServerPasswordHash"] = "" self.passwordHash = "" if cfgOvr != {}: self.launcher.overwrite_launcher_config(cfgOvr) self.launcher.refresh_launcher_config() settings = { 'debug': True, "static_path": self.assetDir, "cookie_secret": self.cookieSecret, "login_url": "/login", "ui_modules": UIModules } handlers = [(r'/', MainHandler, dict(path=settings['static_path'], launcher=self.launcher)), (r"/login", LoginHandler, dict(path=settings['static_path'], launcher=self.launcher)), (r'/logout', LogoutHandler, dict(launcher=self.launcher)), (r"/api", APIRequestHandler, dict(launcher=self.launcher)), (r"/api/savegame", SaveRequestHandler, dict(launcher=self.launcher)), (r"/api/reboot", RebootRequestHandler, dict(launcher=self.launcher)), (r"/api/shutdown", ShutdownRequestHandler, dict(launcher=self.launcher)), (r"/api/player", PlayerRequestHandler, dict(launcher=self.launcher)), ] super().__init__(handlers, **settings)
def save_reporting(self): if self.saveObserver: if not self.saveObserver.is_alive(): self.saveObserver = None self.save_reporting() else: self.saveObserver = Observer() saveGamePath = r"Astro\Saved\SaveGames" watchPath = os.path.join(self.astroPath, saveGamePath) try: if not os.path.exists(watchPath): os.makedirs(watchPath) except Exception as e: AstroLogging.logPrint(e) self.saveObserver.schedule(self.SaveHandler(self), watchPath) self.saveObserver.start()
def print_container(self): """ Displays the saves of a container Arguments: None Returns: None Exception: None """ AstroLogging.logPrint('Extracted save list :') for i, save in enumerate(self.save_list): AstroLogging.logPrint(f'\t {str(i+1)}) {save.save_name}')
def getXauth(self): if self.lastXAuth is None or (datetime.datetime.now() - self.lastXAuth).total_seconds() > 3600: try: gxAuth = None while gxAuth is None: try: AstroLogging.logPrint("Generating new xAuth...", "debug") gxAuth = AstroAPI.generate_XAUTH( self.settings.ServerGuid) except: time.sleep(10) self.launcher.headers['X-Authorization'] = gxAuth self.lastXAuth = datetime.datetime.now() except: self.lastXAuth += datetime.timedelta(seconds=20)
def deregister_server(lobbyID, headers): try: url = f"https://5EA1.playfabapi.com/Client/ExecuteCloudScript?sdk={base_headers['X-PlayFabSDK']}" requestObj = { "FunctionName": "deregisterDedicatedServer", "FunctionParameter": { "lobbyId": lobbyID }, "GeneratePlayStreamEvent": True } AstroLogging.logPrint(requestObj, "debug") x = (AstroRequests.post(url, headers=headers, json=requestObj)).json() AstroLogging.logPrint(x, "debug") return x except: return {"status": "Error"}
def check_container_path(path): """ Defines the container to use Find every *container* file in the given path and make the user chose one Arguments: path -- path where to look for the containers Returns: Returns the chosen container filename Exception: Raise FileNotFoundError if no container is found """ list_containers = [] list_containers = get_container_list(path) if len(list_containers) == 0: raise FileNotFoundError if len(list_containers) == 1: container_name = list_containers[0] else: min_container_number = 1 max_container_number = len(list_containers) container_index = 0 while container_index == 0: AstroLogging.logPrint( '\nWhich container would you like to convert ?') container_index = input() try: container_index = int(container_index) if (container_index < min_container_number or container_index > max_container_number): raise ValueError except ValueError: container_index = 0 AstroLogging.logPrint( f'Please use only values between {min_container_number} and {max_container_number}' ) container_name = list_containers[container_index - 1] return container_name
def on_created(self, event): # print(event) # time.sleep(1) try: time.sleep(0.5) dirName = os.path.dirname(event.src_path) fileNames = [ os.path.join(dirName, f) for f in os.listdir(dirName) if os.path.isfile(os.path.join(dirName, f)) ] # print(fileNames) fileName = sorted(fileNames, key=os.path.getmtime, reverse=True)[0] AstroLogging.logPrint( f"Server saved. {os.path.basename(fileName)}") except: pass