def FindAvailableDLC(gamepath): Log("Finding the next increment in DLC folders...") dlcs = [] DeleteUnusedDlcs(gamepath) # go through each file in the gamepath for file in os.listdir(gamepath): # find all the folders that start with "portal2_dlc" if file.startswith("portal2_dlc") and os.path.isdir(gamepath + GVars.nf + file): # get everything after "portal2_dlc" try: dlcnumber = file.split("portal2_dlc")[1] except Exception as e: Log("Error getting DLC name! Probably a slice error. Moving on!" ) Log("Error: " + str(e)) # move on to the next file continue # if dlcnumber contains any letters, it's not a number if any(char.isalpha() for char in dlcnumber): Log("DLC " + dlcnumber + " is not a number!") else: dlcs.append(str(dlcnumber)) Log("Adding DLC: " + dlcnumber + " to our internal list to ignore...") # sort each dlc number lower to higher dlcs.sort(key=int) # return the folder where to mount the mod return "portal2_dlc" + str(int(dlcs[len(dlcs) - 1]) + 1)
def UnRenameBinaries(gamepath, binarys): # binarys = [ # "bin/linux32/engine.so", # "bin/engine.dll", # "portal2/bin/linux32/server.so", # "portal2/bin/server.dll", # ] Log("") Log("Un-renaming binaries...") # Go through the list of binaries for Og_binary in binarys: # Add a ".p2mmoverride" file extension to the end of the binary's name binary = Og_binary + ".p2mmoverride" # If the binary exists if (os.path.isfile(gamepath + GVars.nf + binary)): Log("Un-renaming " + binary + " to " + Og_binary) # If a file with the original binary's name exist delete it if (os.path.isfile(gamepath + GVars.nf + Og_binary)): os.remove(gamepath + GVars.nf + Og_binary) # Rename the binary back to it's original name os.rename(gamepath + GVars.nf + binary, gamepath + GVars.nf + Og_binary)
def CheckForUpdates(): clientUpdate = up.CheckForNewClient() if clientUpdate["status"]: Log(clientUpdate["name"]) Log(clientUpdate["message"]) valid = False while not valid: update = input("type YES or NO to update") if (update.upper() == "YES") or (update.upper() == "Y"): valid = True if up.DownloadClient("cli") == False: Log("there was an error while updating") Log("please contact the developers") elif (update.upper() == "NO") or (update.upper() == "N"): valid = True if up.CheckForNewFiles(): valid = False while not valid: Log("") update = input( "there are new files do you want to update the mod? DO NOT UPDATE! IF YOU ARE NOT A DEVELOPER! (y/n) " ) if (update.upper() == "YES") or (update.upper() == "Y"): valid = True up.DownloadNewFiles() elif (update.upper() == "NO") or (update.upper() == "N"): valid = True return False
def UnRenameBinaries(gamepath, binarys): # binarys = [ # "bin/linux32/engine.so", # "bin/engine.dll", # "portal2/bin/linux32/server.so", # "portal2/bin/server.dll", # ] Log("") Log("Un-renaming binaries...") # go through the list of binaries for binary in binarys: # add a ".override" to the end of the binary binary = binary + ".override" # if the binary exists if (os.path.isfile(gamepath + GVars.nf + binary)): Log("Un-renaming " + binary + " to " + binary[:-9]) # if a file with the name gamepath + GVars.nf + binary[:-9] exists if (os.path.isfile(gamepath + GVars.nf + binary[:-9])): # remove the file os.remove(gamepath + GVars.nf + binary) else: # rename the binary back to the original os.rename(gamepath + GVars.nf + binary, gamepath + GVars.nf + binary[:-9])
def CheckForNewClient(): Log("searching for a new client...") endpoint = "https://api.github.com/repos" # github's api endpoint try: # do the get request to retrieve the latest release data r = requests.get( f"{endpoint}/{ownerName}/{repoName}/releases/latest").json() except Exception as e: Log(f"error retrieving the latest releases: {str(e)}") return {"status": False} if not "tag_name" in r: return {"status": False} # make sure that the latest release has a different version than the current one and is not a beta release if (currentVersion == r["tag_name"]) or ("beta" in r["tag_name"]): Log("found release but it's old") return {"status": False} results = { "status": True, "name": "Client Update", "message": "Would you like to download \n the new client?" } return results
def LaunchGame(gamepath): Log("") Log("Running Game...") # LAUNCH OPTIONS: -applaunch 620 -novid -allowspectators -nosixense +map mp_coop_lobby_3 +developer 918612 -conclearlog -condebug -console -usercon try: if (GVars.iow): # start portal 2 with the launch options and dont wait for it to finish def RunGame(): # start portal 2 with the launch options and dont wait for it to finish subprocess.run([ gamepath + GVars.nf + "portal2.exe", "-novid", "-allowspectators", "-nosixense", "+map mp_coop_lobby_3", "+developer 918612", "+clear", "-conclearlog", "-usercon" ]) Log("Game exited successfully.") # Run The AfterFunction GVars.AfterFunction() # start the game in a new thread thread = threading.Thread(target=RunGame) thread.start() elif (GVars.iol): def RunGame(): def RunSteam(): os.system( "steam -applaunch 620 -novid -allowspectators -nosixense +map mp_coop_lobby_3 +developer 918612 +clear -conclearlog -usercon" ) threading.Thread(target=RunSteam).start() def CheckForGame(): shouldcheck = True lached = False while shouldcheck: gamerunning = str(os.system("pidof portal2_linux")) if gamerunning == "256": if lached == True: GVars.AfterFunction() shouldcheck = False elif not lached: lached = True time.sleep(1) CheckForGame() thread = threading.Thread(target=RunGame) thread.start() except Exception as e: Log("Failed to launch Portal 2!") Log("Error: " + str(e)) quit()
def VerifyModFiles(): modFilesPath = GVars.modPath + GVars.nf + "ModFiles" + GVars.nf + "Portal 2" + GVars.nf + "install_dlc" Log("searching for mod files in: " + modFilesPath) if (os.path.exists(modFilesPath)) and ( os.path.exists(modFilesPath + GVars.nf + "32playermod.identifier")): Log("Mod files found") return True Log("Mod files not found") return False
def WriteConfigFile(configs): filepath = FindConfigPath() # just to make sure the file doesn't exist try: os.remove(filepath) Log("Deleted old file") except Exception as e: Log(f"Config file doesn't exist? {str(e)}") Log("Writing to file...") with open(filepath, "w", encoding="utf-8") as cfg: json.dump(configs, cfg)
def Encrypt(path, search, replace): enc = "utf-8" rt = "r" wt = "w" # if its a directory if os.path.isdir(path): # loop through every directory and file in the directory for root, dirs, files in os.walk(path): # if the file is a file for file in files: try: ############### f = open(root + GVars.nf + file, rt, encoding=enc) ############### data = f.read() f.close() data = data.replace(search, replace) ############### f = open(root + GVars.nf + file, wt, encoding=enc) ############### f.write(data) f.close() # Log("Encrypted file: " + os.path.join(root, file)) except: # Log("=======ERROR======") # Log("Could not encrypt file: " + os.path.join(root, file)) # Log("=======ERROR======") pass elif os.path.isfile(path): try: Log("Encrypting file: " + file + " With encoding: " + enc + " and read mode: " + rt + " and write mode: " + wt) ############### f = open(root + GVars.nf + file, rt, encoding=enc) ############### data = f.read() f.close() data = data.replace(search, replace) ############### f = open(root + GVars.nf + file, wt, encoding=enc) ############### f.write(data) f.close() Log("Encrypted file: " + os.path.join(root, file)) except: Log("Could not encrypt file: " + os.path.join(root, file))
def VerifyGamePath(): Log("Verifying game path...") valid = False while valid == False: gamepath = GVars.configData["portal2path"]["value"] if ((os.path.exists(gamepath)) != True) or ( os.path.exists(gamepath + GVars.nf + "portal2_dlc2") != True): Log("Game path is invalid!") GetGamePath() else: valid = True Log("Game path is valid!")
def DEVMOUNT(): try: # delete the old modfiles shutil.rmtree(GVars.modPath + GVars.nf + "ModFiles") except Exception as e: Log("folder doesn't exist: " + GVars.modPath + GVars.nf + "ModFiles") Log(str(e)) # copy the one in the current directory to the modpath shutil.copytree(cwd + GVars.nf + "ModFiles", GVars.modPath + GVars.nf + "ModFiles") Log("copied files to mod path")
def IsNew(): if len(sys.argv) != 3: return if (sys.argv[1] != "updated") or (not os.path.exists(sys.argv[2])): return Log("this is first launch after update") Log("deleting old client...") os.remove(sys.argv[2]) Log("renaming new client...") # only change the name between quotes to whatever you want the client to name itself os.rename(GVars.executable, sys.argv[2])
def VerifyConfigFileIntegrity(config): if len(config) != len( DefaultConfigFile2) or config.keys() != DefaultConfigFile2.keys(): DefaultCopy = DefaultConfigFile2 Log("some config data is invalid, fixing it...") for key in DefaultCopy: try: DefaultCopy[key] = config[key] except: Log(f"the key [{key}] is missing from the local config file") Log("writting to file...") WriteConfigFile(DefaultCopy) return DefaultCopy # if the config keys are the same as the default then just return else: return config
def DownloadClient(cType=""): # cType is the Client Type (gui / cli) Log("Downloading...") cType = cType.upper() endpoint = "https://api.github.com/repos" # github's api endpoint r = requests.get( f"{endpoint}/{ownerName}/{repoName}/releases/latest").json() # so we can easily edit it in the future if we want to if (GVars.iow): packageType = ".EXE" elif (GVars.iol): packageType = ".SH" downloadLink = "" # this goes through all the binaries in the latest release until one of them ends with the package type (.exe, .pkg etc...) for i in range(len(r["assets"])): if (r["assets"][i]["browser_download_url"].upper().endswith( cType + packageType)): Log("Found new client to download!") downloadLink = r["assets"][i]["browser_download_url"] break # make sure there's a download link if downloadLink == "": return False # download the file in the same directory # i don't want to bother with folders path = os.path.dirname(GVars.executable) + GVars.nf + "p2mm" + packageType urllib.request.urlretrieve(downloadLink, path) Log(f"Downloaded new client in: {path}") # if (GVars.iow): # command = [path, "updated", GVars.executable] # subprocess.Popen(command) # \n was here, how DARE you misspell linux if (GVars.iol): Log("Linux detected, gotta chmod that bad boy") permissioncommand = "chmod +x " + path os.system(permissioncommand) command = path + " updated " + GVars.executable subprocess.Popen(command, shell=True) Log("launched the new client")
def init(): global appStartDate, modPath, iow, iol, nf appStartDate = datetime.now().strftime('%Y-%m-%d %H-%M-%S') modPath = os.path.dirname(__main__.__file__) if (sys.platform == "win32"): nf = "\\" iow = True elif (sys.platform.startswith("linux")): iol = True nf = "/" else: # feel sad for the poor people who are running templeOS :( Log("This operating system is not supported!") Log("We only support Windows and Linux as of current.") quit()
def ImportConfig(): try: Log(" __________Config Data Start__________") Log("Importing Config...") configpath = FindConfigPath() # if the file doesn't exist then create it if not os.path.exists(configpath): WriteConfigFile(DefaultConfigFile) # read all the lines in the config file config = open(configpath, "r", encoding="utf-8").read().strip() # if the file is empty then re-create it if len(config) == 0: WriteConfigFile(DefaultConfigFile) # process the config file into useable data Log("Processing config...") Log("") processedconfig = json.loads(config) Log("") Log("Configs imported successfully!") # at last pass the data to the verify function to make sure everything is clean return VerifyConfigFileIntegrity(processedconfig) except Exception as e: Log(f"Error importing the config file: {str(e)}") WriteConfigFile(DefaultConfigFile) GVars.hadtoresetconfig = True return ImportConfig()
def RunGame(): # start portal 2 with the launch options and dont wait for it to finish subprocess.run([ gamepath + GVars.nf + "portal2.exe", "-novid", "-allowspectators", "-nosixense", "+map mp_coop_lobby_3", "+developer 918612", "+clear", "-conclearlog", "-usercon" ]) Log("Game exited successfully.") # Run The AfterFunction GVars.AfterFunction()
def UserAction(): validinput = False global WillMount WillMount = True while (not validinput): ShouldMount = input( "(?) Would you like to mount or unmount the mod? (Mount/Unmount) ") # if the user doesn't want to proceed they can can quit if (ShouldMount.upper() == "EXIT" or ShouldMount.upper() == "ABORT" or ShouldMount.upper() == "QUIT"): Log("Exiting...") quit() if (ShouldMount.upper() == "MOUNT" or ShouldMount.upper() == "M"): validinput = True Log("User input: " + ShouldMount) Log("Mounting the mod...") elif (ShouldMount.upper() == "UNMOUNT" or ShouldMount.upper() == "U"): validinput = True WillMount = False Log("User input: " + ShouldMount) Log("Unmounting the mod...") else: Log("Type in a valid option!")
def ValidatePlayerKeys(): Log("validating keys...") try: indx = -1 errs = 0 for player in GVars.configData["Players"]["value"]: if player.keys() != defaultplayerarray.keys(): indx += 1 errs += 1 defaultPlayer = defaultplayerarray Log(f"found {str(errs)} key errors") print( f"local keys = {player.keys()} \n saved keys = {defaultPlayer.keys()}") for key in defaultPlayer: try: defaultPlayer[key] = player[key] except Exception as e: Log(str(e)) GVars.configData["Players"]["value"][indx] = defaultPlayer if errs > 0: WriteConfigFile(GVars.configData) else: Log("all keys are validated") except Exception as e: Log("ERROR: " + str(e)) Log("ERROR: Players is not a list, resetting to default") GVars.configData["Players"]["value"] = [defaultplayerarray] WriteConfigFile(GVars.configData)
def UnpatchBinaries(gamepath): binarys = [ "bin" + GVars.nf + "linux32" + GVars.nf + "engine.so", "bin" + GVars.nf + "engine.dll", "portal2" + GVars.nf + "bin" + GVars.nf + "linux32" + GVars.nf + "server.so", "portal2" + GVars.nf + "bin" + GVars.nf + "server.dll", ] Log("") Log(" __________Binary Restoration_________") Log("Unpatching binaries...") for binary in binarys: # get the filename filename = binary.rsplit(GVars.nf, 1)[1] # delete the file from the gamepath if it exitsts if (os.path.isfile(gamepath + GVars.nf + filename)): Log("Deleting " + gamepath + GVars.nf + filename + "...") os.remove(gamepath + GVars.nf + filename) # unrename the binaries so we can move them UnRenameBinaries(gamepath, binarys)
def init(): global appStartDate, modPath, iow, iol, nf, configPath appStartDate = datetime.now().strftime('%Y-%m-%d %H-%M-%S') if (sys.platform == "win32"): iow = True # again thanks stackOverflow for this # this code allows us to get the document's folder on any windows pc with any language CSIDL_PERSONAL = 5 # My Documents SHGFP_TYPE_CURRENT = 0 # Get current, not default value buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH) ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None, SHGFP_TYPE_CURRENT, buf) # set the modpath to the users documents folder modPath = buf.value + nf + "p2mm" configPath = buf.value + nf + "p2mm" elif (sys.platform.startswith("linux")): iol = True # set the modpath the the users home directory modPath = os.path.expanduser("~") + nf + ".cache/p2mm" configPath = os.path.expanduser("~") + nf + ".config/p2mm" else: # feel sad for the poor people who are running templeOS :( Log("This operating system is not supported!") Log("We only support Windows and Linux as of current.") quit() # check if the modpath exists, if not create it if not os.path.exists(modPath): os.makedirs(modPath) if not os.path.exists(configPath): os.makedirs(configPath)
def DeleteUnusedDlcs(gamepath): Log("") Log(" _________Dealing with Folders________") if ((os.path.exists(gamepath)) != True) or ( os.path.exists(gamepath + GVars.nf + "portal2_dlc2") != True): Log("Portal 2 game path not found!") return "undefined" # go through each file in the gamepath for file in os.listdir(gamepath): # find all the folders that start with "portal2_dlc" if file.startswith("portal2_dlc") and os.path.isdir(gamepath + GVars.nf + file): # if inside the folder there is a file called "32playermod.identifier" delete this folder if "32playermod.identifier" in os.listdir(gamepath + GVars.nf + file): Log("Found old DLC: " + file) # delete the folder even if it's not empty BF.DeleteFolder(gamepath + GVars.nf + file) Log("Deleted old DLC: " + file) return True
def WriteConfigFile(configs): filepath = FindConfigPath() # just to make sure the file doesn't exist try: os.remove(filepath) except: Log("doesn't matter") cfg = open(filepath, "w", encoding="utf-8") for key in configs: cfg.write(key + " = " + configs[key] + "\n") cfg.close()
def RenameBinaries(gamepath, binarys): # binarys = [ # "bin/linux32/engine.so", # "bin/engine.dll", # "portal2/bin/linux32/server.so", # "portal2/bin/server.dll", # ] # go through the list of binaries for binary in binarys: # if the binary exists if (os.path.isfile(gamepath + GVars.nf + binary)): # add a ".override" to the end of the binary os.rename(gamepath + GVars.nf + binary, gamepath + GVars.nf + binary + ".override") Log("Renamed " + binary + " to " + binary + ".override")
def VerifyConfigFileIntegrity(config): # if len(config) != len(DefaultConfigFile) or config.keys() != DefaultConfigFile.keys(): # DefaultCopy = DefaultConfigFile # Log("Some config data is invalid, fixing it...") # for key in DefaultCopy: # try: # DefaultCopy[key] = config[key] # except: # Log(f"The key [{key}] is missing from the local config file!") # WriteConfigFile(DefaultCopy) # return DefaultCopy Log("=========================") Log("Validating config data...") copiedconfig = { **config, } # VALIDATE ALL THE KEYS ARE CORRECT for key1 in copiedconfig: if key1 not in DefaultConfigFile: Log(f"The key [{key1}] is invalid, fixing it...") config.pop(key1) WriteConfigFile(config) continue for key2 in config[key1]: # validate all the immutable values if key2 in ImmutableKeys: if config[key1][key2] != DefaultConfigFile[key1][key2]: Log(f"The value for [{key1}][{key2}] is invalid, fixing it...") config[key1][key2] = DefaultConfigFile[key1][key2] WriteConfigFile(config) # VALIDATE ALL THE KEYS EXIST for key in DefaultConfigFile: if key not in config: Log(f"The key [{key}] is missing, fixing it...") config[key] = DefaultConfigFile[key] WriteConfigFile(config) # VALIDATE THAT ALL THE KEYS HAVE ALL THE VALUES for key in DefaultConfigFile: for key2 in DefaultConfigFile[key]: if key2 not in config[key]: Log(f"The value for [{key}][{key2}] is missing, fixing it...") config[key][key2] = DefaultConfigFile[key][key2] WriteConfigFile(config) Log("=========================") # if the config keys are the same as the default then just return them return config
def MountMod(gamepath): # make sure the gamepath in the config file exists and is valid if (gamepath == "undefined") or ((os.path.exists(gamepath)) != True) or ( os.path.exists(gamepath + GVars.nf + "portal2_dlc2") != True): Log("Portal 2 Path not found!") return "undefined" # detect if the mod files are available modFilesPath = os.path.dirname( __main__.__file__ ) + GVars.nf + "ModFiles" + GVars.nf + "Portal 2" + GVars.nf + "install_dlc" if os.path.exists(modFilesPath): Log("MultiplayerModFiles folder exists!") else: Log("MultiplayerModFiles folder not found!") return "filesMissing" Log("") Log(" __________Mounting Mod Start_________") Log("Gathering DLC folder data...") # find a place to mount the dlc dlcmountpoint = FindAvailableDLC(gamepath) destination = shutil.copytree(modFilesPath + GVars.nf + ".", gamepath + GVars.nf + dlcmountpoint) Log("copied the mod files successfully to " + destination) # patch the binaries Log(" ___________Moving Files End__________") UnpatchBinaries(gamepath) PatchBinaries(gamepath) Log(" __________Mounting Mod End__________") return True
def MountMod(gamepath, encrypt=False): Log("") Log(" __________Mounting Mod Start_________") Log("Gathering DLC folder data...") modFilesPath = GVars.modPath + GVars.nf + "ModFiles" + GVars.nf + "Portal 2" + GVars.nf + "install_dlc" # find a place to mount the dlc dlcmountpoint = FindAvailableDLC(gamepath) destination = BF.CopyFolder(modFilesPath + GVars.nf + ".", gamepath + GVars.nf + dlcmountpoint) Log("Successfully copied the mod files to " + destination) nutConfigFile = gamepath + GVars.nf + dlcmountpoint + GVars.nf + "scripts" + GVars.nf + "vscripts" + GVars.nf + "multiplayermod" + GVars.nf + "config.nut" if os.path.exists(nutConfigFile): SetVscriptConfigFile(nutConfigFile) #### ENCRYPT THE CVARS ##### Log("____ENCRYPTING CVARS____") if encrypt: SetNewEncryptions() else: UnEncryptEncryptions() path = gamepath + GVars.nf + dlcmountpoint if encrypt: for cmdrep in CommandReplacements: EncryptCVars.Encrypt(BF.ConvertPath(path), cmdrep[1], cmdrep[2]) Log("__ENCRYPTING CVARS END__") # patch the binaries Log(" ___________Moving Files End__________") UnpatchBinaries(gamepath) PatchBinaries(gamepath) Log(" ___________Mounting Mod End__________") return True
def CheckForNewFiles(): if not haveInternet(): Log("No internet Connection") return False Log("Checking for new files...") # plan # download modIndex.json # check if the date is greater than the one saved in the local identifier file # ask the user if they want to update # if yes read where the files are saved on the github repo # download all the files and delete the old ones # check if the identifier file exists or no localIdPath = GVars.modPath + GVars.nf + f"ModFiles{GVars.nf}Portal 2{GVars.nf}install_dlc{GVars.nf}32playermod.identifier" if not os.path.isfile(localIdPath): Log("identifier file doesn't exist so the mod files are probably unavailable too" ) return True Log("found local identifier file") # if there was an error retrieving this file that means most likely that we changed it's name and released a new client try: r = requests.get( f"https://raw.githubusercontent.com/{ownerName}/{repoName}/main/ModIndex.json" ).json() except Exception as e: Log(f"error getting the index file: {str(e)}") return False # compare the dates of the local file and the file on the repo localDate = datetime.strptime(open(localIdPath, "r").read(), "%Y-%m-%d") remoteDate = datetime.strptime(r["Date"], "%Y-%m-%d") # if the remote date is less or equal to the local date that means our client is up to date if (remoteDate <= localDate): Log("mod files are up to date") return False Log(f"the remote date {remoteDate} is greater than the local date {localDate}" ) return True
def SetNewEncryptions(): # set the new encryptions Log("Setting new encryptions...") Log("") minlen = 3 for cmdrep in CommandReplacements: Log("===========") Log("Original CVAR: " + cmdrep[1]) cmdrep[2] = cmdrep[1][:len(cmdrep[1]) - int( len(cmdrep[1]) / minlen )] + ''.join( random.choice( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ) for i in range(int(len(cmdrep[1]) / minlen))) Log("New CVAR: " + cmdrep[2]) Log("===========")
def Init(): OnStart() Log("") Log("Initializing...") Log("") # ask the user what they want before proceeding UserAction() Log("") #//# mount the multiplayer mod #//# if (WillMount): # checks for the state of the mounting process mountState = "" # undefined -> the game path in the config file is either undefined or invalid # filesMissing -> the mod's files (ModFiles/Portal 2/install_dlc) are missing # true -> the mouting process completed successfully while mountState != True: gamepath = GVars.configData["portal2path"] mountState = RG.MountMod(gamepath) if mountState == "undefined": Log("game path is undefined/ invalid , would you like to select it?") GetGamePath() if mountState == "filesMissing": Log("the mod files are missing") return RG.LaunchGame(gamepath) # launch the game else: mountState = "" # undefined -> the game path in the config file is either undefined or invalid # filesMissing -> the mod's files (ModFiles/Portal 2/install_dlc) are missing # true -> the mouting process completed successfully while mountState != True: gamepath = GVars.configData["portal2path"] mountState = RG.MountMod(gamepath) if mountState == "undefined": Log("game path is undefined/ invalid , would you like to select it?") GetGamePath() RG.DeleteUnusedDlcs(gamepath) RG.UnpatchBinaries(gamepath)