diff --git a/setup b/setup index 72d17a4..17dbb6a 100755 --- a/setup +++ b/setup @@ -62,10 +62,59 @@ def download_file(url, filename): pbar.close() return filename -if __name__ == "__main__": +def version_compare(installed_version, package_version): + if installed_version == package_version: return 0 + installed = installed_version.split(".") + packaged = package_version.split(".") + if installed[0] < packaged[0]: + return 1 + elif installed[0] > packaged[0]: + return -1 + if installed[1] < packaged[1]: + return 1 + elif installed[1] > packaged[1]: + return -1 + if installed[2] < packaged[2]: + return 1 + elif installed[2] > packaged[2]: + return -1 - action=menu(["~~[C]ontinue","[Q]uit"],header="This script will install Tribes 2 for use as a dedicated server.") - if action == "Q": bail() +if __name__ == "__main__": + # Check for an existing install. Read version from the release file if it exists, otherwise assume 0.7.3 + if isfile(release_file): + with open(release_file, 'r') as rf: + installed_version = rf.read().rstrip() + elif isfile(f"{install_dir}/GameData/Tribes2.exe"): + installed_version = "0.7.3" + else: + installed_version = None + + # Get the version in this install package + if isfile(f"{pwd}/etc/t2server/release"): + with open(f"{pwd}/etc/t2server/release", 'r') as rf: + package_version = rf.read().rstrip() + else: + package_version = None + + # Compare versions to see if the install package is newer than the installed version and quit if not + if installed_version and package_version: upgrade = version_compare(installed_version, package_version) + else: upgrade = None + + # Exit if trying to update to an older version or the same version, otherwise display the initial menu and determine the setup mode + if upgrade == -1: + bail(f"This would install t2server {package_version} which is older than what's already installed, version {installed_version}.") + elif upgrade == 0: + bail(f"The existing t2server install is the same version as this install package, {package_version}.") + elif upgrade == 1: + setup_mode=menu([f"[U]pgrade to {package_version}","[Q]uit"],header=f"An existing t2server {installed_version} install was detected. 'Upgrade' will only update the scripts and files that come with t2server and won't reinstall Tribes 2.") + if setup_mode == "Q": bail() + else: + action=menu(["~~[C]ontinue","[Q]uit"],header="This script will install Tribes 2 for use as a dedicated server.") + if action == "Q": bail() + setup_mode="I" + + if setup_mode == "R": + system(f"{pwd}/usr/local/bin/t2remove -Y") # Check if user exists try: @@ -73,226 +122,232 @@ if __name__ == "__main__": except KeyError: user_info = False - # Create or repurpose user - if user_info: - if user_info.pw_dir == install_dir: - pwarn(f"User '{user}' exists and will be reused.") + if setup_mode == "I" or setup_mode == "R": + # Create or repurpose user if installing or reinstalling + if user_info: + if user_info.pw_dir == install_dir: + pwarn(f"User '{user}' exists and will be reused.") + else: + bail(f"ERROR: User '{user}' already exists and may belong to another person or process.") else: - bail(f"ERROR: User '{user}' already exists and may belong to another person or process.") - else: - pinfo(f"Creating {user} user and {install_dir}.") - system(f"useradd -md {install_dir} {user}") + print(f"Creating {user} user and {install_dir}.") + system(f"useradd -md {install_dir} {user}") - if not user_info: user_info = getuser(user) + if not user_info: user_info = getuser(user) - # Create log_dir - pinfo(f"Creating {log_dir}.") - makedirs(log_dir, mode=0o777, exist_ok=True) - chmod(log_dir, 0o777) + # Create log_dir + print(f"Creating {log_dir}.") + makedirs(log_dir, mode=0o777, exist_ok=True) + chmod(log_dir, 0o777) - # Create .wine dir - pinfo(f"Creating {install_dir}/.wine defaults.") - system(f"su - {user} -c'wineboot -i > /dev/null 2>&1'") + # Create .wine dir + print(f"Creating {install_dir}/.wine defaults.") + system(f"su - {user} -c'wineboot -i > /dev/null 2>&1'") - # Map wine I: drive to pwd T: drive to install_dir and L: to log_dir - pinfo(f"Mapping I: in wine for {user}.") - try: - symlink(f"{pwd}/winbin", f"{install_dir}/.wine/dosdevices/i:") - except FileExistsError: - pass - - pinfo(f"Mapping L: in wine for {user}.") - try: - symlink(log_dir, f"{install_dir}/.wine/dosdevices/l:") - except FileExistsError: - pass - - pinfo(f"Mapping T: in wine for {user}.") - try: - symlink(install_parent, f"{install_dir}/.wine/dosdevices/t:") - except FileExistsError: - pass - - - # Check for needed exe/zip/dll files in winbin dir - needed_files=[] - if isfile(f"{pwd}/winbin/tribes2gsi.exe"): - pinfo("tribes2gsi.exe found.") - rename(f"{pwd}/winbin/tribes2gsi.exe",f"{pwd}/winbin/tribes2_gsi.exe") - installer_exe = f"{pwd}/winbin/tribes2_gsi.exe" - elif isfile(f"{pwd}/winbin/tribes2_gsi.exe"): - pinfo("tribes2_gsi.exe found.") - installer_exe = f"{pwd}/winbin/tribes2_gsi.exe" - else: - pwarn("Tribes 2 installer not found.") - needed_files.append("tribes2_gsi.exe") - installer_exe = False - - if isfile(f"{pwd}/winbin/TribesNext_rc2a.exe"): - pinfo("TribesNext_rc2a.exe found.") - tnpatch_exe = f"{pwd}/winbin/TribesNext_rc2a.exe" - else: - pwarn("Tribes Next patch not found.") - needed_files.append("TribesNext_rc2a.exe") - tnpatch_exe = False - - ruby_dll = f"{pwd}/winbin/msvcrt-ruby191.dll" - instwrap_exe = f"{pwd}/winbin/install_wrapper.exe" - - # Download files if needed - if not installer_exe or not tnpatch_exe: - action=menu(["~~[D]ownload automatically","[Q]uit"],header="One or more needed files were not found. Download automatically or quit so they can be manually placed in the 'winbin' subdirectory?") - if needed_files == 2: - needed_files=f"{needed_files[0]} and {needed_files[1]}" - if action=="Q": bail(f"Manually place {needed_files} in the 'winbin' subdirectory then rerun setup.") - - if not installer_exe: - for url in installer_mirror_list: - try: - pinfo(f"\nDownloading from {url.split('/')[2]}...") - installer_exe = download_file(url, f"{pwd}/winbin/tribes2_gsi.exe") - if md5sum(installer_exe) == installer_checksum: - pinfo("Checksum validation passed.") - break - else: - perror("Checksum validation failed. Trying next mirror.") - except KeyError: - perror("Download error. Trying next mirror.") - continue - - if not installer_exe: - bail("ERROR: Tribes 2 installer could not be downloaded.") - - if not tnpatch_exe: - for url in tnpatch_mirror_list: - try: - pinfo(f"\nDownloading from {url.split('/')[2]}...") - tnpatch_exe = download_file(url, f"{pwd}/winbin/TribesNext_rc2a.exe") - if md5sum(tnpatch_exe) == tnpatch_checksum: - pinfo("Checksum validation passed.") - break - else: - perror("Checksum validation failed. Trying next mirror." ) - except KeyError: - perror("Download error. Trying next mirror.") - continue - - if not tnpatch_exe: - bail("ERROR: Tribes Next patch could not be downloaded.") - - # Present SLAs before beginning install - sla = None - while not sla: - sla=menu(["[V]iew Tribes 2 and TribesNext License Agreements", "[A]ccept License Agreements", "[Q]uit"], header="Please take a moment to review and accept the Tribes 2 and TribeNext License Agreements before beginning automated install.") - if sla == "V": - print(color.DY) - system(f"/usr/bin/less {pwd}/sla/tribes2.txt") - print(color.DP) - system(f"/usr/bin/less {pwd}/sla/tribesnext.txt") - sla = None - elif sla == "A": - break - elif sla == "Q": - bail("You must accept the License Agreements to install.") - - # Ensure sufficient permissions on winbin and its contents - chmod(f"{pwd}/winbin", 0o777) - chmod(installer_exe, 0o777) - chmod(tnpatch_exe, 0o777) - chmod(instwrap_exe, 0o777) - chmod(ruby_dll, 0o777) - - # Execute install wrapper - pinfo(f"\nInstalling Tribes 2 and the TribesNext patch in wine. Please wait...") - chowner(install_dir, user) - system(f"su - {user} -c'xvfb-run -as " + '"-fbdir /var/tmp"' + " wine I:/install_wrapper.exe > /dev/null 2>&1'") - - # Rudamentary check to see if T2 install succeeded - if not isfile(f"{install_dir}/Tribes 2 Solo & LAN.lnk"): bail(f"ERROR: Tribes 2 installation appears to have failed. Check {log_dir}/install_wrapper.log") - - # Rudamentary check to see if TN install succeeded - if not isfile(f"{install_dir}/GameData/TN_Uninstall.exe"): bail(f"ERROR: Tribes Next installation appears to have failed. Check {log_dir}/install_wrapper.log") - - # Replace msvcrt-ruby190.dll with msvcrt-ruby191.dll - pinfo("Updating msvcrt-ruby190.dll to msvcrt-ruby191.dll.\n") - copyfile(ruby_dll,f"{install_dir}/GameData/msvcrt-ruby191.dll") - unlink(f"{install_dir}/GameData/msvcrt-ruby190.dll") - symlink(f"{install_dir}/GameData/msvcrt-ruby191.dll", f"{install_dir}/GameData/msvcrt-ruby190.dll") - - # Install addons - for addon in iglob(f"{pwd}/addons/*"): - if addon.endswith(".zip"): - pinfo(f"Unpacking {addon} into {install_dir}/GameData.") - system(f"unzip -qqd {install_dir}/GameData {addon}") - elif addon.endswith((".tar",".tgz",".tar.gz",".txz",".tar.xz",".tbz",".tar.bz")): - pinfo(f"Unpacking {addon} into {install_dir}/GameData.") - system(f"tar -C {install_dir}/GameData -xf {addon}") - elif addon.endswith(".vl2"): - pinfo(f"Copying {addon} to {install_dir}/GameData/base.") - copyfile(addon,f"{install_dir}/GameData/base/{addon.split('/')[-1]}") - elif addon.endswith("readme.txt"): + # Map wine I: drive to pwd T: drive to install_dir and L: to log_dir + print(f"Mapping I: in wine for {user}.") + try: + symlink(f"{pwd}/winbin", f"{install_dir}/.wine/dosdevices/i:") + except FileExistsError: pass + + print(f"Mapping L: in wine for {user}.") + try: + symlink(log_dir, f"{install_dir}/.wine/dosdevices/l:") + except FileExistsError: + pass + + print(f"Mapping T: in wine for {user}.") + try: + symlink(install_parent, f"{install_dir}/.wine/dosdevices/t:") + except FileExistsError: + pass + + # Check for needed exe/zip/dll files in winbin dir + needed_files=[] + if isfile(f"{pwd}/winbin/tribes2gsi.exe"): + pinfo("tribes2gsi.exe found.") + rename(f"{pwd}/winbin/tribes2gsi.exe",f"{pwd}/winbin/tribes2_gsi.exe") + installer_exe = f"{pwd}/winbin/tribes2_gsi.exe" + elif isfile(f"{pwd}/winbin/tribes2_gsi.exe"): + pinfo("tribes2_gsi.exe found.") + installer_exe = f"{pwd}/winbin/tribes2_gsi.exe" else: - pwarn(f"Ignoring {addon}.") + pwarn("Tribes 2 installer not found.") + needed_files.append("tribes2_gsi.exe") + installer_exe = False + + if isfile(f"{pwd}/winbin/TribesNext_rc2a.exe"): + pinfo("TribesNext_rc2a.exe found.") + tnpatch_exe = f"{pwd}/winbin/TribesNext_rc2a.exe" + else: + pwarn("Tribes Next patch not found.") + needed_files.append("TribesNext_rc2a.exe") + tnpatch_exe = False + + ruby_dll = f"{pwd}/winbin/msvcrt-ruby191.dll" + instwrap_exe = f"{pwd}/winbin/install_wrapper.exe" + + # Download files if needed + if not installer_exe or not tnpatch_exe: + action=menu(["~~[D]ownload automatically","[Q]uit"],header="One or more needed files were not found. Download automatically or quit so they can be manually placed in the 'winbin' subdirectory?") + if needed_files == 2: + needed_files=f"{needed_files[0]} and {needed_files[1]}" + if action=="Q": bail(f"Manually place {needed_files} in the 'winbin' subdirectory then rerun setup.") + + if not installer_exe: + for url in installer_mirror_list: + try: + pinfo(f"\nDownloading from {url.split('/')[2]}...") + installer_exe = download_file(url, f"{pwd}/winbin/tribes2_gsi.exe") + if md5sum(installer_exe) == installer_checksum: + print("Checksum validation passed.") + break + else: + perror("Checksum validation failed. Trying next mirror.") + except KeyError: + perror("Download error. Trying next mirror.") + continue + + if not installer_exe: + bail("ERROR: Tribes 2 installer could not be downloaded.") + + if not tnpatch_exe: + for url in tnpatch_mirror_list: + try: + pinfo(f"\nDownloading from {url.split('/')[2]}...") + tnpatch_exe = download_file(url, f"{pwd}/winbin/TribesNext_rc2a.exe") + if md5sum(tnpatch_exe) == tnpatch_checksum: + print("Checksum validation passed.") + break + else: + perror("Checksum validation failed. Trying next mirror." ) + except KeyError: + perror("Download error. Trying next mirror.") + continue + + if not tnpatch_exe: + bail("ERROR: Tribes Next patch could not be downloaded.") + + # Present SLAs before beginning install + sla = None + while not sla: + sla=menu(["[V]iew Tribes 2 and TribesNext License Agreements", "[A]ccept License Agreements", "[Q]uit"], header="Please take a moment to review and accept the Tribes 2 and TribeNext License Agreements before beginning automated install.") + if sla == "V": + print(color.DY) + system(f"/usr/bin/less {pwd}/sla/tribes2.txt") + print(color.DP) + system(f"/usr/bin/less {pwd}/sla/tribesnext.txt") + sla = None + elif sla == "A": + break + elif sla == "Q": + bail("You must accept the License Agreements to install.") + + # Ensure sufficient permissions on winbin and its contents + chmod(f"{pwd}/winbin", 0o777) + chmod(installer_exe, 0o777) + chmod(tnpatch_exe, 0o777) + chmod(instwrap_exe, 0o777) + chmod(ruby_dll, 0o777) + + # Execute install wrapper + pinfo(f"\nInstalling Tribes 2 and the TribesNext patch in wine. Please wait...") + chowner(install_dir, user) + system(f"su - {user} -c'xvfb-run -as " + '"-fbdir /var/tmp"' + " wine I:/install_wrapper.exe > /dev/null 2>&1'") + + # Rudamentary check to see if T2 install succeeded + if not isfile(f"{install_dir}/Tribes 2 Solo & LAN.lnk"): bail(f"ERROR: Tribes 2 installation appears to have failed. Check {log_dir}/install_wrapper.log") + + # Rudamentary check to see if TN install succeeded + if not isfile(f"{install_dir}/GameData/TN_Uninstall.exe"): bail(f"ERROR: Tribes Next installation appears to have failed. Check {log_dir}/install_wrapper.log") + + # Replace msvcrt-ruby190.dll with msvcrt-ruby191.dll + print("Updating msvcrt-ruby190.dll to msvcrt-ruby191.dll.\n") + copyfile(ruby_dll,f"{install_dir}/GameData/msvcrt-ruby191.dll") + unlink(f"{install_dir}/GameData/msvcrt-ruby190.dll") + symlink(f"{install_dir}/GameData/msvcrt-ruby191.dll", f"{install_dir}/GameData/msvcrt-ruby190.dll") + + # Install addons + for addon in iglob(f"{pwd}/addons/*"): + if addon.endswith(".zip"): + pinfo(f"Unpacking {addon} into {install_dir}/GameData.") + system(f"unzip -qqd {install_dir}/GameData {addon}") + elif addon.endswith((".tar",".tgz",".tar.gz",".txz",".tar.xz",".tbz",".tar.bz")): + pinfo(f"Unpacking {addon} into {install_dir}/GameData.") + system(f"tar -C {install_dir}/GameData -xf {addon}") + elif addon.endswith(".vl2"): + pinfo(f"Copying {addon} to {install_dir}/GameData/base.") + copyfile(addon,f"{install_dir}/GameData/base/{addon.split('/')[-1]}") + elif addon.endswith("readme.txt"): + pass + else: + pwarn(f"Ignoring {addon}.") # Copy t2server and t2bouncer to /usr/local/bin/ - pinfo("Installing t2server script.") + if setup_mode == "U": print("Updating t2server script.") + else: print("Installing t2server script.") copyfile(f"{pwd}/usr/local/bin/t2server",f"{bin_dir}/t2server") - pinfo("Installing t2bouncer script.") + + if setup_mode == "U": print("Updating t2bouncer script.") + else: print("Installing t2bouncer script.") copyfile(f"{pwd}/usr/local/bin/t2bouncer",f"{bin_dir}/t2bouncer") # Set owner/group on install_dir chowner(install_dir, user) - # Clean up temp dir and some unneeded files - pinfo("A little housekeeping...") - if isfile(f"{install_dir}/Tribes 2 Online.lnk"): unlink(f"{install_dir}/Tribes 2 Online.lnk") - if isfile(f"{install_dir}/Tribes 2 Solo & LAN.lnk"): unlink(f"{install_dir}/Tribes 2 Solo & LAN.lnk") - if isfile(f"{install_dir}/UNWISE.EXE"): unlink(f"{install_dir}/UNWISE.EXE") - if isfile(f"{install_dir}/Readme.txt"): unlink(f"{install_dir}/Readme.txt") - if isfile(f"{install_dir}/GameData/Classic_LAN.bat"): unlink(f"{install_dir}/GameData/Classic_LAN.bat") - if isfile(f"{install_dir}/GameData/Classic_dedicated_server.bat"): unlink(f"{install_dir}/GameData/Classic_dedicated_server.bat") - if isfile(f"{install_dir}/GameData/Classic_online.bat"): unlink(f"{install_dir}/GameData/Classic_online.bat") - if isfile(f"{install_dir}/GameData/base/EULA.txt"): unlink(f"{install_dir}/GameData/base/EULA.txt") - if isfile(f"{install_dir}/GameData/base/UKEULA.txt"): unlink(f"{install_dir}/GameData/base/UKEULA.txt") - if isdir(f"{install_dir}/Manual"): rmtree(f"{install_dir}/Manual") - if isdir(f"{install_dir}/.wine/drive_c/users/t2server/Temp"): rmtree(f"{install_dir}/.wine/drive_c/users/t2server/Temp") - if isfile(f"{install_dir}/t2csri_eula.txt"): unlink(f"{install_dir}/t2csri_eula.txt") - if isfile(f"{install_dir}/Inside\ Team\ Rabbit\ 2.txt"): unlink(f"{install_dir}/Inside\ Team\ Rabbit\ 2.txt") - if isfile(f"{install_dir}/UpdatePatch.txt"): unlink(f"{install_dir}/UpdatePatch.txt") - if isfile(f"{install_dir}/Classic/Classic_readme.txt"): unlink(f"{install_dir}/Classic/Classic_readme.txt") - if isfile(f"{install_dir}/Classic_technical.txt"): unlink(f"{install_dir}/Classic_technical.txt") + if setup_mode == "I" or setup_mode == "R": + # Clean up temp dir and some unneeded files + print("A little housekeeping...") + if isfile(f"{install_dir}/Tribes 2 Online.lnk"): unlink(f"{install_dir}/Tribes 2 Online.lnk") + if isfile(f"{install_dir}/Tribes 2 Solo & LAN.lnk"): unlink(f"{install_dir}/Tribes 2 Solo & LAN.lnk") + if isfile(f"{install_dir}/UNWISE.EXE"): unlink(f"{install_dir}/UNWISE.EXE") + if isfile(f"{install_dir}/Readme.txt"): unlink(f"{install_dir}/Readme.txt") + if isfile(f"{install_dir}/GameData/Classic_LAN.bat"): unlink(f"{install_dir}/GameData/Classic_LAN.bat") + if isfile(f"{install_dir}/GameData/Classic_dedicated_server.bat"): unlink(f"{install_dir}/GameData/Classic_dedicated_server.bat") + if isfile(f"{install_dir}/GameData/Classic_online.bat"): unlink(f"{install_dir}/GameData/Classic_online.bat") + if isfile(f"{install_dir}/GameData/base/EULA.txt"): unlink(f"{install_dir}/GameData/base/EULA.txt") + if isfile(f"{install_dir}/GameData/base/UKEULA.txt"): unlink(f"{install_dir}/GameData/base/UKEULA.txt") + if isdir(f"{install_dir}/Manual"): rmtree(f"{install_dir}/Manual") + if isdir(f"{install_dir}/.wine/drive_c/users/t2server/Temp"): rmtree(f"{install_dir}/.wine/drive_c/users/t2server/Temp") + if isfile(f"{install_dir}/t2csri_eula.txt"): unlink(f"{install_dir}/t2csri_eula.txt") + if isfile(f"{install_dir}/Inside\ Team\ Rabbit\ 2.txt"): unlink(f"{install_dir}/Inside\ Team\ Rabbit\ 2.txt") + if isfile(f"{install_dir}/UpdatePatch.txt"): unlink(f"{install_dir}/UpdatePatch.txt") + if isfile(f"{install_dir}/Classic/Classic_readme.txt"): unlink(f"{install_dir}/Classic/Classic_readme.txt") + if isfile(f"{install_dir}/Classic_technical.txt"): unlink(f"{install_dir}/Classic_technical.txt") # Create config directory and files - pinfo(f"\nCreating {etc_dir}, default config, and installing prefs files.") + print(f"\nCreating {etc_dir}, default config, and installing prefs files.") makedirs(f"{etc_dir}/serverprefs", mode=0o775, exist_ok=True) if isfile(f"{etc_dir}/config.yaml"): timestamp = int(time()) rename(f"{etc_dir}/config.yaml",f"{etc_dir}/config.yaml.{timestamp}") pwarn(f"Existing {etc_dir}/config.yaml renamed to {etc_dir}/config.yaml.{timestamp}. Be sure to compare with and update the new config.yaml file.") - pinfo(f"Writing default {etc_dir}/config.yaml.") + print(f"Writing default {etc_dir}/config.yaml.") copyfile(f"{pwd}/etc/t2server/config.yaml", f"{etc_dir}/config.yaml") + print(f"Writing {etc_dir}/release") + copyfile(f"{pwd}/etc/t2server/release", f"{etc_dir}/release") for pfile in iglob(f"{pwd}/etc/t2server/serverprefs/*"): pinfo(f"Copying {pfile} to {etc_dir}/serverprefs.") copyfile(pfile,f"{etc_dir}/serverprefs/{pfile.split('/')[-1]}") # Create systemd units - pinfo("\nCreating systemd units:") - pinfo("- t2server service") + print("\nCreating systemd units:") + print("- t2server service") copyfile(f"{pwd}/etc/systemd/system/t2server.service",f"{unit_dir}/t2server.service") - pinfo("- t2bouncer service") + print("- t2bouncer service") copyfile(f"{pwd}/etc/systemd/system/t2bouncer.service",f"{unit_dir}/t2bouncer.service") - pinfo("- t2bouncer timer") + print("- t2bouncer timer") copyfile(f"{pwd}/etc/systemd/system/t2bouncer.timer",f"{unit_dir}/t2bouncer.timer") system("systemctl daemon-reload") # Install utility scripts - pinfo("\nInstalling utilities:") - pinfo("- t2bouncer") + print("\nInstalling utilities:") + print("- t2bouncer") copyfile(f"{pwd}/usr/local/bin/t2fixer",f"{bin_dir}/t2fixer") - pinfo("- t2remove") + print("- t2remove") copyfile(f"{pwd}/usr/local/bin/t2remove",f"{bin_dir}/t2remove") - pinfo("- t2help") + print("- t2help") copyfile(f"{pwd}/usr/local/bin/t2help",f"{bin_dir}/t2help") # Install python module @@ -302,6 +357,6 @@ if __name__ == "__main__": # Show help system(f"{bin_dir}/t2help") - menu(['~~[E]xit'],header="You can run 't2help' at any time to view the info above again.") + pinfo("You can run 't2help' at any time to view the info above again.") print(f"{color.X}\n") \ No newline at end of file