├── 7z.zip ├── LICENSE ├── LLConfigMaker.pyw ├── LLGameStarter.pyw ├── LLUpdater.py ├── LLWindows.pyw ├── LanLauncher.pyw ├── README.MD ├── ThirdParty-Licenses ├── 7zip-license.txt └── pyinstaller-license.txt ├── bin ├── LAN.ico ├── LLUpdater.exe └── LanLauncher.exe └── update.ini /7z.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JugAndDoubleTap/LanLauncher/14424acd8c7e71572661e2a552226648ba346e49/7z.zip -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /LLConfigMaker.pyw: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def GenerateConfig(GENERAL, settings, serverid, configName, selectedMap, multiplayerMode, prer4516): 4 | configType = '' 5 | 6 | match serverid: 7 | case "World at War": 8 | path = settings[GENERAL["PLUTONIUMINSTANCE"]] + fr'/storage/t4/main/' 9 | configType = "t4" 10 | case "Black ops": 11 | path = settings[GENERAL["PLUTONIUMINSTANCE"]] + fr'/storage/t5/main/' 12 | configType = "t5" 13 | case "Black ops II": 14 | path = settings[GENERAL["PLUTONIUMINSTANCE"]] + fr'/storage/t6/main/' 15 | configType = "t6" 16 | case "Modern Warfare 3": 17 | path = settings[GENERAL["PLUTONIUMINSTANCE"]] + fr'/storage/iw5/main/' 18 | configType = "iw5" 19 | multiplayerMode = True 20 | 21 | 22 | if multiplayerMode: 23 | configName = "mp_" + configName.replace(" ", "_") 24 | configType = configType + "mp" 25 | else: 26 | configName = "zm_" + configName.replace(" ", "_") 27 | configType = configType + "zm" 28 | 29 | if prer4516: 30 | configName = configName + "_Pre_r4516" 31 | 32 | 33 | match configType: 34 | case "t4mp": 35 | mapRotation = 'set sv_maprotationcurrent ""' 36 | case "t4zm": 37 | mapRotation = 'set sv_maprotationcurrent ""' 38 | case "t5mp": 39 | mapRotation = '' 40 | case "t5zm": 41 | mapRotation = '' 42 | case "t6mp": 43 | mapRotation = 'map_rotate' 44 | case "t6zm": 45 | mapRotation = 'map_rotate' 46 | case "iw5mp": 47 | mapRotation = '' 48 | 49 | 50 | cfg = rf'''set sp_minplayers 1 51 | set g_password "" 52 | set rcon_rate_limit "500" 53 | 54 | rconWhitelistAdd "127.0.0.1" 55 | rconWhitelistAdd "192.168.0.7" 56 | rconWhitelistAdd "10.0.0.12" 57 | rconWhitelistAdd "172.16.8.7" 58 | 59 | set sv_maxclients "4" 60 | set sv_maxRate "25000" 61 | set sv_pure "0" 62 | set scr_game_spectatetype "1" 63 | set g_gravity "800" 64 | set g_speed "190" 65 | set bullet_penetration_affected_by_team false 66 | set perk_weapRateEnhanced false 67 | 68 | set rate "25000" 69 | set g_antilag "1" 70 | set sv_fps "20" 71 | 72 | set sv_allowDownload "0" 73 | set sv_wwwDownload "0" 74 | set sv_wwwBaseURL "" 75 | set sv_wwwDlDisconnected "0" 76 | 77 | set g_log "{configName}.log" 78 | set g_logSync "2" 79 | set logfile "2" 80 | set sv_kickBanTime "300" 81 | 82 | set fire_audio_random_max_duration "1000" 83 | set fire_audio_repeat_duration "1500" 84 | set fire_spread_probability "0" 85 | set fire_stage1_burn_time "3000" 86 | set fire_stage2_burn_time "0" 87 | set fire_stage3_burn_time "0" 88 | set fire_world_damage "20" 89 | set fire_world_damage_duration "8" 90 | set fire_world_damage_rate "0.25" 91 | set flareDisableEffects "0" 92 | 93 | demo_enabled 0 94 | 95 | set sv_mapRotation "{selectedMap}" 96 | {mapRotation}''' 97 | 98 | with open(f'{path}{configName}.cfg', 'w') as file: 99 | file.write(cfg) 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /LLGameStarter.pyw: -------------------------------------------------------------------------------- 1 | import os, subprocess 2 | import LLWindows as LLW 3 | 4 | def Launch(GENERAL, settings, modSelection): 5 | exePath = "/bin/plutonium-bootstrapper-win32.exe" 6 | 7 | if not settings[GENERAL["USERNAME"]] != "": 8 | LLW.ErrorWindow(GENERAL, settings, "username") 9 | # cancels launching if the username is blank 10 | return 11 | 12 | if not os.path.exists(settings[GENERAL["PLUTONIUMINSTANCE"]] + exePath): 13 | LLW.ErrorWindow(GENERAL, settings, "plutonium") 14 | # cancels launching if the directory for plutonium is invalid 15 | return 16 | 17 | match settings[GENERAL["GAMEID"]]: 18 | case "World at War": 19 | settings[GENERAL["ACTIVEGAME"]] = settings[GENERAL["WAW"]] 20 | if not os.path.isfile(settings[GENERAL["ACTIVEGAME"]] + "/main/iw_00.iwd"): 21 | LLW.ErrorWindow(GENERAL, settings, "T4") 22 | # cancels launching if the directory for world at war is invalid 23 | return 24 | 25 | case "Black ops": 26 | settings[GENERAL["ACTIVEGAME"]] = settings[GENERAL["BO1"]] 27 | if not os.path.isfile(settings[GENERAL["ACTIVEGAME"]] + "/main/iw_00.iwd"): 28 | LLW.ErrorWindow(GENERAL, settings, "T5") 29 | # cancels launching if the directory for black ops is invalid 30 | return 31 | 32 | case "Black ops II": 33 | settings[GENERAL["ACTIVEGAME"]] = settings[GENERAL["BO2"]] 34 | if not os.path.isfile(settings[GENERAL["ACTIVEGAME"]] + "/zone/all/base.ipak"): 35 | LLW.ErrorWindow(GENERAL, settings, "T6") 36 | # cancels launching if the directory for black ops 2 is invalid 37 | return 38 | 39 | case "Modern Warfare 3": 40 | settings[GENERAL["ACTIVEGAME"]] = settings[GENERAL["MW3"]] 41 | if not os.path.isfile(settings[GENERAL["ACTIVEGAME"]] + "/main/iw_00.iwd"): 42 | LLW.ErrorWindow(GENERAL, settings, "IW5") 43 | # cancels launching if the directory for modern warfare 3 is invalid 44 | return 45 | 46 | # default launch with no mods selected 47 | launchPlutonium = rf'"{settings[GENERAL["PLUTONIUMINSTANCE"]] + exePath}" {settings[GENERAL["MODEID"]]} "{settings[GENERAL["ACTIVEGAME"]]}" +name "{settings[GENERAL["USERNAME"]]}" -lan' 48 | 49 | if modSelection != '': 50 | if not settings[GENERAL["GAMEID"]] == settings[GENERAL["MODID"]]: 51 | LLW.ErrorWindow(GENERAL, settings, "wrongGame") 52 | # cancels launching if the mod is selected for the wrong game 53 | return 54 | else: 55 | # sets to launch with a mod 56 | modSelection = fr'"mods/{modSelection}"' 57 | launchPlutonium = rf'"{settings[GENERAL["PLUTONIUMINSTANCE"]] + exePath}" {settings[GENERAL["MODEID"]]} "{settings[GENERAL["ACTIVEGAME"]]}" +name "{settings[GENERAL["USERNAME"]]}" -lan +set fs_game {modSelection}' 58 | 59 | subprocess.Popen(launchPlutonium, cwd=settings[GENERAL["PLUTONIUMINSTANCE"]]) 60 | 61 | 62 | def LaunchServer(GENERAL, settings, modSelection, configSelection, port): 63 | exePath = "/bin/plutonium-bootstrapper-win32.exe" 64 | 65 | 66 | if not os.path.exists(settings[GENERAL["PLUTONIUMINSTANCE"]] + exePath): 67 | LLW.ErrorWindow(GENERAL, settings, "plutonium") 68 | # cancels launching if the directory for plutonium is invalid 69 | return 70 | 71 | match settings[GENERAL["SERVERID"]]: 72 | case "World at War": 73 | settings[GENERAL["ACTIVEGAME"]] = settings[GENERAL["WAW"]] 74 | if settings[GENERAL["WAWSERVMULT"]] == False: 75 | settings[GENERAL["WAWSERVMULT"]] == "+set zombiemode 1" 76 | else: 77 | settings[GENERAL["WAWSERVMULT"]] == "" 78 | if not os.path.isfile(settings[GENERAL["ACTIVEGAME"]] + "/main/iw_00.iwd"): 79 | LLW.ErrorWindow(GENERAL, settings, "T4") 80 | # cancels launching if the directory for world at war is invalid 81 | return 82 | 83 | case "Black ops": 84 | settings[GENERAL["ACTIVEGAME"]] = settings[GENERAL["BO1"]] 85 | if not os.path.isfile(settings[GENERAL["ACTIVEGAME"]] + "/main/iw_00.iwd"): 86 | LLW.ErrorWindow(GENERAL, settings, "T5") 87 | # cancels launching if the directory for black ops is invalid 88 | return 89 | 90 | case "Black ops II": 91 | if not os.path.exists(settings[GENERAL["PLUTONIUMINSTANCE"]] + '/storage/t6/gamesettings'): 92 | LLW.ErrorWindow(GENERAL, settings, "prompt to download gamesettings") 93 | return 94 | 95 | settings[GENERAL["ACTIVEGAME"]] = settings[GENERAL["BO2"]] 96 | if not os.path.isfile(settings[GENERAL["ACTIVEGAME"]] + "/zone/all/base.ipak"): 97 | LLW.ErrorWindow(GENERAL, settings, "T6") 98 | # cancels launching if the directory for black ops 2 is invalid 99 | return 100 | 101 | case "Modern Warfare 3": 102 | settings[GENERAL["ACTIVEGAME"]] = settings[GENERAL["MW3"]] 103 | if not os.path.isfile(settings[GENERAL["ACTIVEGAME"]] + "/main/iw_00.iwd"): 104 | LLW.ErrorWindow(GENERAL, settings, "IW5") 105 | # cancels launching if the directory for modern warfare 3 is invalid 106 | return 107 | 108 | # default launch with no mods selected 109 | launchServer = rf'"{settings[GENERAL["PLUTONIUMINSTANCE"]] + exePath}" {settings[GENERAL["MODEID"]]} "{settings[GENERAL["ACTIVEGAME"]]}" -lan -dedicated {settings[GENERAL["WAWSERVMULT"]]} +exec {configSelection} +set net_port {port} +map_rotate' 110 | 111 | if modSelection != '': 112 | if not settings[GENERAL["SERVERID"]] == settings[GENERAL["MODID"]]: 113 | LLW.ErrorWindow(GENERAL, settings, "wrongGameServer") 114 | # cancels launching if the mod is selected for the wrong game 115 | return 116 | else: 117 | # sets to launch with a mod 118 | modSelection = fr'"mods/{modSelection}"' 119 | launchServer = rf'"{settings[GENERAL["PLUTONIUMINSTANCE"]] + exePath}" {settings[GENERAL["MODEID"]]} "{settings[GENERAL["ACTIVEGAME"]]}" -lan -dedicated {settings[GENERAL["WAWSERVMULT"]]} +set fs_game {modSelection} +exec {configSelection} +set net_port {port} +map_rotate' 120 | 121 | subprocess.Popen(launchServer, cwd=settings[GENERAL["PLUTONIUMINSTANCE"]]) 122 | -------------------------------------------------------------------------------- /LLUpdater.py: -------------------------------------------------------------------------------- 1 | import FreeSimpleGUI as sg 2 | import subprocess, wget, sys, os 3 | 4 | 5 | errorvar = 0 6 | 7 | def tryhard(): 8 | try: 9 | os.remove('LAN.ico') 10 | except Exception: 11 | pass 12 | try: 13 | os.rename(r".LANTMP.ico", "LAN.ico") 14 | except Exception: 15 | pass 16 | try: 17 | os.remove(r'LanLauncher.exe') 18 | except Exception: 19 | pass 20 | try: 21 | os.rename(r'.LanLauncherTMP.exe', 'LanLauncher.exe') 22 | except Exception: 23 | pass 24 | subprocess.Popen('LanLauncher.exe', shell=True) 25 | sys.exit() 26 | 27 | def error_message(): 28 | sg.set_options(font=('Cambria', 10)) 29 | sg.theme('DarkAmber') 30 | errorlay = [[sg.Text('Could not update LanLauncher.')], 31 | [sg.Button('Close')]] 32 | errorwin = sg.Window('', errorlay) 33 | 34 | error = 0 35 | while error == 0: 36 | event, values = errorwin.read() 37 | if event == 'Close' or event == sg.WIN_CLOSED: 38 | error += 1 39 | errorwin.close() 40 | sys.exit() 41 | try: 42 | wget.download(r'https://github.com/JugAndDoubleTap/LanLauncher/raw/main/bin/LanLauncher.exe', '.LanLauncherTMP.exe') 43 | wget.download(r'https://raw.githubusercontent.com/JugAndDoubleTap/LanLauncher/main/bin/LAN.ico', ".LANTMP.ico") 44 | except Exception: 45 | errorvar = 1 46 | error_message() 47 | if errorvar == 0: 48 | tryhard() 49 | -------------------------------------------------------------------------------- /LLWindows.pyw: -------------------------------------------------------------------------------- 1 | import os, wget, subprocess, sys, configparser, shutil, socket 2 | import FreeSimpleGUI as sg 3 | from zipfile import ZipFile 4 | import LanLauncher as LL 5 | import LLGameStarter as LLG 6 | import LLConfigMaker as LLC 7 | 8 | def MainWindow(GENERAL, settings): 9 | configList = [''] 10 | modList = [''] 11 | 12 | sg.theme(settings[GENERAL["THEME"]]) 13 | sg.set_options(font=('Cambria', 10)) 14 | serverTab = [ 15 | [sg.Text('Select game to host server for', size=(23, 1)), sg.Text('', size=(7, 1)), sg.Button('Refresh config list'), sg.Button('Deselect config')], 16 | [ 17 | sg.InputCombo(['World at War', 'Black ops II', 'Modern Warfare 3'], key='gameselectserver', enable_events=True, readonly=True), 18 | sg.Text('', size=(9, 1)), 19 | sg.Text(f'Available configs for {settings[GENERAL["GAMEID"]]}', key='configtitle') 20 | ], 21 | [ 22 | sg.Column( 23 | [ 24 | [sg.Radio("ZM", key='serverzombies', default=True, group_id='gamemodeserver')], 25 | [sg.Radio("MP", key='servermultiplayer', group_id='gamemodeserver')], 26 | [sg.Text("Port number")], 27 | [sg.InputText('5000', key="port", size=(10, 2)), sg.Text('', size=(18,1))], 28 | [sg.Text("Your local IP address")], 29 | [sg.Text(socket.gethostbyname(socket.gethostname()), font=("Cambria", 10, "bold"))], 30 | ], 31 | vertical_alignment='top', 32 | pad=(0, 0) 33 | ), 34 | sg.Listbox(configList, size=(30, 10), key='configlist'), 35 | ], 36 | [sg.Text("")], 37 | [sg.Text('', size=(48, 1)), sg.Button('Launch server')], 38 | [sg.Text('', size=(35, 1)), sg.Button('Delete config '), sg.Button('Make a config')], 39 | ] 40 | settingsTab = [[sg.Text('Theme Selection')], 41 | [sg.InputCombo(['DarkAmber', 42 | 'DarkPurple2', 43 | 'DarkBlack', 44 | 'DarkTeal10', 45 | 'LightBrown9', 46 | 'SandyBeach', 47 | 'DarkTanBlue'], key='themeselect', enable_events=True, readonly=True)]] 48 | 49 | 50 | 51 | modsTab = [[sg.Text('Select game to see mods for', size=(22,1)), sg.Text('', size=(10,1)),sg.Button('Refresh mod list'), sg.Button('Deselect mod ')], 52 | [sg.InputCombo(['World at War', 'Black ops', 'Black ops II', 'Modern Warfare 3'], key='gameselectmod', enable_events=True, readonly=True),sg.Text('', size=(9,1)), sg.Text(f'Available mods for {settings[GENERAL["GAMEID"]]}', key='modtitle') ], 53 | [sg.Text('', size=(30,1)), sg.Listbox(modList, size=(30, 10), key='modlist'), sg.Text('')], 54 | [sg.Text('', size=(30,1)), sg.Text('Install mods here')], 55 | [sg.Text('', size=(30,1)), sg.InputText('Mod in Zip, Rar, 7z, or exe', size=(21,1), key='modtoinstall'), sg.FileBrowse('Browse', key='modtoinstall', file_types=(("Zip Files", "*.zip"), ("Rar Files", "*.rar"), ("7z Files", "*.7z"), ("Executable Files", "*.exe")))], 56 | [sg.Text('', size=(38,1)),sg.Button('Delete mod '), sg.Button('Install mod')] 57 | ] 58 | 59 | mainTab = [[sg.Text('Username', pad=((0, 0), (0, 0)))], 60 | [sg.InputText(settings[GENERAL["USERNAME"]], key='username', pad=((0, 0), (0, 0))), sg.Radio(r"SP/ZM", key='zombies', default=True, group_id='gamemode', pad=((61, 0), (0, 0)))], 61 | [sg.Text('Plutonium folder (AppData)', pad=((0, 0), (0, 0)))], 62 | [sg.InputText(settings[GENERAL["PLUTONIUMINSTANCE"]], key='plutoniuminstance', pad=((0, 0), (0, 0))), sg.FolderBrowse(key='plutoniumfolder'), sg.Radio("MP", key='multiplayer', group_id='gamemode', pad=((0, 0), (0, 0)))], 63 | [sg.Text('World at War folder', pad=((0, 0), (0, 0)))], 64 | [sg.InputText(settings[GENERAL["WAW"]], key='waw', pad=((0, 0), (0, 0))), sg.FolderBrowse(key='waw'), sg.Button('Launch T4')], 65 | [sg.Text('Black ops folder ', pad=((0, 0), (0, 0)))], 66 | [sg.InputText(settings[GENERAL["BO1"]], key='bo1', pad=((0, 0), (0, 0))), sg.FolderBrowse(key='bo1'), sg.Button('Launch T5')], 67 | [sg.Text('Black ops II folder ', pad=((0, 0), (0, 0)))], 68 | [sg.InputText(settings[GENERAL["BO2"]], key='bo2', pad=((0, 0), (0, 0))), sg.FolderBrowse(key='bo2'), sg.Button('Launch T6')], 69 | [sg.Text('ModernWarfare 3 folder', pad=((0, 0), (0, 0)))], 70 | [sg.InputText(settings[GENERAL["MW3"]], key='mw3', pad=((0, 0), (0, 0))), sg.FolderBrowse(key='mw3'), sg.Button('Launch IW5')], 71 | ] 72 | 73 | mainLayout = [[sg.TabGroup([[sg.Tab('Main', mainTab), sg.Tab('Mods', modsTab), sg.Tab('Lan Server', serverTab), sg.Tab('Misc', settingsTab)]])], 74 | [sg.Button('Close'), sg.Button('Save Settings'), sg.Button('Update'), sg.Button('Help'), sg.Text('Made By JugAndDoubleTap', pad=((0, 0), (0, 0))), sg.Text('V' + settings[GENERAL["VERSIONNUM"]], pad=((20, 0), (0, 0)))] 75 | ] 76 | mainWindow = sg.Window(r'LanLauncher (for Plutonium)', mainLayout, icon=('LAN.ico')) 77 | windowLoop = True 78 | # loop so that the window stays running for button presses and other GUI features 79 | while (windowLoop == True): 80 | event, values = mainWindow.read() 81 | # switch statement for GUI button actions 82 | match event: 83 | case sg.WIN_CLOSED: 84 | windowLoop = False 85 | mainWindow.close() 86 | case "Close": 87 | LL.SaveToINI(GENERAL, settings, values) 88 | windowLoop = False 89 | mainWindow.close() 90 | case "Save Settings": 91 | LL.SaveToINI(GENERAL, settings, values) 92 | 93 | case "themeselect": 94 | if values['themeselect'] != '': 95 | settings[GENERAL["THEME"]] = values['themeselect'] 96 | LL.SaveToINI(GENERAL, settings, values) 97 | LL.LoadFromINI(GENERAL, settings) 98 | windowLoop = False 99 | mainWindow.close() 100 | MainWindow(GENERAL, settings) 101 | case "Help": 102 | ErrorWindow(GENERAL, settings, "help") 103 | case "Update": 104 | UpdateWindow(GENERAL, settings, values) 105 | case "Launch T4": 106 | if values["multiplayer"] == True: 107 | settings[GENERAL["MODEID"]] = 't4mp' 108 | else: 109 | settings[GENERAL["MODEID"]] = 't4sp' 110 | 111 | settings[GENERAL["GAMEID"]] = 'World at War' 112 | LL.SaveToINI(GENERAL, settings, values) 113 | LL.LoadFromINI(GENERAL, settings) 114 | if values["modlist"] != []: 115 | LLG.Launch(GENERAL, settings, values["modlist"][0]) 116 | else: 117 | LLG.Launch(GENERAL, settings, '') 118 | 119 | case "Launch T5": 120 | if values["multiplayer"] == True: 121 | settings[GENERAL["MODEID"]] = 't5mp' 122 | else: 123 | settings[GENERAL["MODEID"]] = 't5sp' 124 | 125 | settings[GENERAL["GAMEID"]] = 'Black ops' 126 | LL.SaveToINI(GENERAL, settings, values) 127 | LL.LoadFromINI(GENERAL, settings) 128 | if values["modlist"] != []: 129 | LLG.Launch(GENERAL, settings, values["modlist"][0]) 130 | else: 131 | LLG.Launch(GENERAL, settings, '') 132 | 133 | case "Launch T6": 134 | if values["multiplayer"] == True: 135 | settings[GENERAL["MODEID"]] = 't6mp' 136 | else: 137 | settings[GENERAL["MODEID"]] = 't6zm' 138 | 139 | settings[GENERAL["GAMEID"]] = 'Black ops II' 140 | LL.SaveToINI(GENERAL, settings, values) 141 | LL.LoadFromINI(GENERAL, settings) 142 | if values["modlist"] != []: 143 | LLG.Launch(GENERAL, settings, values["modlist"][0]) 144 | else: 145 | LLG.Launch(GENERAL, settings, '') 146 | 147 | case "Launch IW5": 148 | if values["multiplayer"] == True: 149 | settings[GENERAL["MODEID"]] = 'IW5mp' 150 | else: 151 | settings[GENERAL["MODEID"]] = 'IW5mp' 152 | 153 | settings[GENERAL["GAMEID"]] = 'Modern Warfare 3' 154 | LL.SaveToINI(GENERAL, settings, values) 155 | LL.LoadFromINI(GENERAL, settings) 156 | if values["modlist"] != []: 157 | LLG.Launch(GENERAL, settings, values["modlist"][0]) 158 | else: 159 | LLG.Launch(GENERAL, settings, '') 160 | 161 | case "gameselectmod": 162 | match values['gameselectmod']: 163 | case 'World at War': 164 | path = values["plutoniuminstance"] + fr'\storage\t4\mods' 165 | settings[GENERAL["MODID"]] = 'World at War' 166 | mainWindow['modtitle'].update(f'Available mods for {settings[GENERAL["MODID"]]}') 167 | 168 | 169 | 170 | case 'Black ops': 171 | path = values["plutoniuminstance"] + fr'\storage\t5\mods' 172 | settings[GENERAL["MODID"]] = 'Black ops' 173 | mainWindow['modtitle'].update(f'Available mods for {settings[GENERAL["MODID"]]}') 174 | 175 | 176 | 177 | case 'Black ops II': 178 | path = values["plutoniuminstance"] + fr'\storage\t6\mods' 179 | settings[GENERAL["MODID"]] = 'Black ops II' 180 | mainWindow['modtitle'].update(f'Available mods for {settings[GENERAL["MODID"]]}') 181 | 182 | 183 | 184 | case 'Modern Warfare 3': 185 | path = values["plutoniuminstance"] + fr'\storage\iw5\mods' 186 | settings[GENERAL["MODID"]] = 'Modern Warfare 3' 187 | mainWindow['modtitle'].update(f'Available mods for {settings[GENERAL["MODID"]]}') 188 | 189 | 190 | modList = [] 191 | mainWindow['modlist'].update(values=modList) 192 | if os.path.exists(path): 193 | modList = [f for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))] 194 | mainWindow['modlist'].update(values=modList) 195 | 196 | if not os.path.exists(path): 197 | os.mkdir(path) 198 | 199 | case "Deselect mod " | "Refresh mod list": 200 | match settings[GENERAL["MODID"]]: 201 | case 'World at War': 202 | path = values["plutoniuminstance"] + fr'/storage/t4/mods' 203 | case 'Black ops': 204 | path = values["plutoniuminstance"] + fr'/storage/t5/mods' 205 | case 'Black ops II': 206 | path = values["plutoniuminstance"] + fr'/storage/t6/mods' 207 | case 'Modern Warfare 3': 208 | path = values["plutoniuminstance"] + fr'/storage/iw5/mods' 209 | 210 | modList = [] 211 | mainWindow['modlist'].update(values=[]) 212 | if os.path.exists(path): 213 | modList = [f for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))] 214 | mainWindow['modlist'].update(values=modList) 215 | 216 | case "Install mod": 217 | abort = False 218 | if os.path.isdir(os.getcwd() + "/7z"): 219 | match settings[GENERAL["MODID"]]: 220 | case "World at War": 221 | path = values["plutoniuminstance"] + fr'/storage/t4/mods' 222 | case "Black ops": 223 | path = values["plutoniuminstance"] + fr'/storage/t5/mods' 224 | case "Black ops II": 225 | path = values["plutoniuminstance"] + fr'/storage/t6/mods' 226 | case "Modern Warfare 3": 227 | path = values["plutoniuminstance"] + fr'/storage/iw5/mods' 228 | case _: 229 | abort = True 230 | 231 | 232 | else: 233 | abort = True 234 | ErrorWindow(GENERAL, settings, '7z') 235 | if abort == False: 236 | result = LL.ExtractArchive(path, values['modtoinstall']) 237 | match result: 238 | case "standard mod format": 239 | if os.path.exists(path): 240 | modList = [f for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))] 241 | mainWindow['modlist'].update(values=modList) 242 | case "not standard mod format": 243 | ErrorWindow(GENERAL, settings, result) 244 | case "not supported extension": 245 | ErrorWindow(GENERAL, settings, result) 246 | case "failed to install mod": 247 | ErrorWindow(GENERAL, settings, result) 248 | 249 | 250 | case "Delete mod ": 251 | abort = False 252 | match settings[GENERAL["MODID"]]: 253 | case "World at War": 254 | path = values["plutoniuminstance"] + fr'/storage/t4/mods' 255 | case "Black ops": 256 | path = values["plutoniuminstance"] + fr'/storage/t5/mods' 257 | case "Black ops II": 258 | path = values["plutoniuminstance"] + fr'/storage/t6/mods' 259 | case "Modern Warfare 3": 260 | path = values["plutoniuminstance"] + fr'/storage/iw5/mods' 261 | case _: 262 | abort = True 263 | if abort != True: 264 | modToDelete = '' 265 | try: 266 | modToDelete = values['modlist'][0] 267 | except Exception as e: 268 | print(e) 269 | if modToDelete != '': 270 | try: 271 | shutil.rmtree(path + "/" + modToDelete) 272 | except Exception as e: 273 | print(e) 274 | 275 | mainWindow['modlist'].update(values=[]) 276 | if os.path.exists(path): 277 | modList = [f for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))] 278 | mainWindow['modlist'].update(values=modList) 279 | else: 280 | ErrorWindow(GENERAL, settings, 'noMod') 281 | 282 | case "gameselectserver": 283 | match values['gameselectserver']: 284 | case "World at War": 285 | path = values["plutoniuminstance"] + fr'\storage\t4\main' 286 | settings[GENERAL["SERVERID"]] = 'World at War' 287 | mainWindow['configtitle'].update(f'Available configs for {settings[GENERAL["SERVERID"]]}') 288 | 289 | 290 | 291 | case "Black ops": 292 | path = values["plutoniuminstance"] + fr'\storage\t5\main' 293 | settings[GENERAL["SERVERID"]] = 'Black ops' 294 | mainWindow['configtitle'].update(f'Available configs for {settings[GENERAL["SERVERID"]]}') 295 | 296 | 297 | 298 | case "Black ops II": 299 | path = values["plutoniuminstance"] + fr'\storage\t6\main' 300 | settings[GENERAL["SERVERID"]] = 'Black ops II' 301 | mainWindow['configtitle'].update(f'Available configs for {settings[GENERAL["SERVERID"]]}') 302 | 303 | 304 | 305 | case "Modern Warfare 3": 306 | path = values["plutoniuminstance"] + fr'\storage\iw5\main' 307 | settings[GENERAL["SERVERID"]] = 'Modern Warfare 3' 308 | mainWindow['configtitle'].update(f'Available configs for {settings[GENERAL["SERVERID"]]}') 309 | 310 | configlist = [] 311 | mainWindow['configlist'].update(values=configlist) 312 | 313 | if os.path.exists(path): 314 | configlist = [f for f in os.listdir(path) if f.endswith('.cfg')] 315 | mainWindow['configlist'].update(values=configlist) 316 | 317 | if not os.path.exists(path): 318 | os.mkdir(path) 319 | 320 | case "Deselect config" | "Refresh config list": 321 | abort = False 322 | match values["gameselectserver"]: 323 | 324 | 325 | case "World at War": 326 | path = values["plutoniuminstance"] + fr'\storage\t4\main' 327 | settings[GENERAL["SERVERID"]] = 'World at War' 328 | mainWindow['configtitle'].update(f'Available configs for {settings[GENERAL["SERVERID"]]}') 329 | 330 | 331 | 332 | case "Black ops": 333 | path = values["plutoniuminstance"] + fr'\storage\t5\main' 334 | settings[GENERAL["SERVERID"]] = 'Black ops' 335 | mainWindow['configtitle'].update(f'Available configs for {settings[GENERAL["SERVERID"]]}') 336 | 337 | 338 | 339 | case "Black ops II": 340 | path = values["plutoniuminstance"] + fr'\storage\t6\main' 341 | settings[GENERAL["SERVERID"]] = 'Black ops II' 342 | mainWindow['configtitle'].update(f'Available configs for {settings[GENERAL["SERVERID"]]}') 343 | 344 | 345 | 346 | case "Modern Warfare 3": 347 | path = values["plutoniuminstance"] + fr'\storage\iw5\main' 348 | settings[GENERAL["SERVERID"]] = 'Modern Warfare 3' 349 | mainWindow['configtitle'].update(f'Available configs for {settings[GENERAL["SERVERID"]]}') 350 | 351 | case _: 352 | abort = True 353 | if abort != True: 354 | configList = [] 355 | mainWindow['configlist'].update(values=configList) 356 | 357 | if os.path.exists(path): 358 | configList = [f for f in os.listdir(path) if f.endswith('.cfg')] 359 | mainWindow['configlist'].update(values=configList) 360 | 361 | case "Delete config ": 362 | abort = False 363 | match values["gameselectserver"]: 364 | 365 | case "World at War": 366 | path = values["plutoniuminstance"] + fr'/storage/t4/main' 367 | case "Black ops": 368 | path = values["plutoniuminstance"] + fr'/storage/t5/main' 369 | case "Black ops II": 370 | path = values["plutoniuminstance"] + fr'/storage/t6/main' 371 | case "Modern Warfare 3": 372 | path = values["plutoniuminstance"] + fr'/storage/iw5/main' 373 | case _: 374 | abort = True 375 | if abort != True: 376 | cfgToDelete = '' 377 | try: 378 | cfgToDelete = values['configlist'][0] 379 | except Exception as e: 380 | print(e) 381 | if cfgToDelete != '': 382 | try: 383 | os.remove(path + "/" + cfgToDelete) 384 | except Exception as e: 385 | print(e) 386 | 387 | mainWindow['configlist'].update(values=[]) 388 | if os.path.exists(path): 389 | configlist = [f for f in os.listdir(path) if f.endswith('.cfg')] 390 | mainWindow['configlist'].update(values=configlist) 391 | else: 392 | ErrorWindow(GENERAL, settings, "noCFG") 393 | case "Make a config": 394 | LL.SaveToINI(GENERAL, settings, values) 395 | LL.LoadFromINI(GENERAL, settings) 396 | MakeConfigWindow(GENERAL, settings, values["gameselectserver"], values["servermultiplayer"], mainWindow) 397 | 398 | case "Launch server": 399 | match settings[GENERAL["SERVERID"]]: 400 | case "World at War": 401 | if values["servermultiplayer"] == True: 402 | settings[GENERAL["WAWSERVMULT"]] = True 403 | settings[GENERAL["MODEID"]] = 't4mp' 404 | else: 405 | settings[GENERAL["WAWSERVMULT"]] = False 406 | settings[GENERAL["MODEID"]] = 't4sp' 407 | case "Black ops": 408 | if values["servermultiplayer"] == True: 409 | settings[GENERAL["MODEID"]] = 't5mp' 410 | else: 411 | settings[GENERAL["MODEID"]] = 't5sp' 412 | case "Black ops II": 413 | if values["servermultiplayer"] == True: 414 | settings[GENERAL["MODEID"]] = 't6mp' 415 | else: 416 | settings[GENERAL["MODEID"]] = 't6zm' 417 | case "Modern Warfare 3": 418 | settings[GENERAL["MODEID"]] = 'iw5mp' 419 | 420 | LL.SaveToINI(GENERAL, settings, values) 421 | LL.LoadFromINI(GENERAL, settings) 422 | if values["configlist"] == []: 423 | ErrorWindow(GENERAL, settings, "noCFG") 424 | else: 425 | if values["modlist"] != []: 426 | LLG.LaunchServer(GENERAL, settings, values["modlist"][0], values["configlist"][0], values["port"]) 427 | else: 428 | LLG.LaunchServer(GENERAL, settings, '', values["configlist"][0], values["port"]) 429 | 430 | 431 | 432 | def ErrorWindow(GENERAL, settings, errorType): 433 | sg.theme(settings[GENERAL["THEME"]]) 434 | sg.set_options(font=('Cambria', 10)) 435 | message = '' 436 | 437 | errorLayout = [ [sg.Text(message, key='message')], 438 | [sg.Button('Close')]] 439 | 440 | match errorType: 441 | case "help": 442 | message = 'The folder refers to the location at which your game is installed.\nFor example, if you have it installed to C:/games/pluto_t6_fullgame\nThen that is what you would either type or browse for.' 443 | case "username": 444 | message = 'The username you selected is invalid.' 445 | case "T4": 446 | message = f'The specified folder "{settings[GENERAL["WAW"]]}" \ndoes not contain valid World at War game data.' 447 | case "T5": 448 | message = f'The specified folder "{settings[GENERAL["BO1"]]}" \ndoes not contain valid Black ops game data.' 449 | case "T6": 450 | message = f'The specified folder "{settings[GENERAL["BO2"]]}" \ndoes not contain valid Black ops II game data.' 451 | case "IW5": 452 | message = f'The specified folder "{settings[GENERAL["MW3"]]}" \ndoes not contain valid Modern Warfare 3 game data.' 453 | case "plutonium": 454 | message = f'The specified folder "{settings[GENERAL["PLUTONIUMINSTANCE"]]}" \ndoes not contain valid Plutonium launcher data.' 455 | case "wrongGame": 456 | message = f'You have selected a mod for {settings[GENERAL["MODID"]]}, \nbut you tried to launch {settings[GENERAL["GAMEID"]]}.' 457 | case "wrongGameServer": 458 | message = f'You have selected a mod for {settings[GENERAL["MODID"]]}, \nbut you tried to launch a {settings[GENERAL["SERVERID"]]} server.' 459 | case "noMod": 460 | message = 'You do not have a mod selected.' 461 | case "noCFG": 462 | message = 'You do not have a config selected.' 463 | case "not supported extension": 464 | message = 'The mod you tried to install does not have a supported file extension.' 465 | case "not standard mod format": 466 | message = 'The mod you tried to install does not use the standard mod file structure,\nand was not installed.' 467 | case "failed to install mod": 468 | message = 'Failed to install the mod.' 469 | case "7z": 470 | message = 'You currently do not have 7-zip installed in the LanLauncher Directory,\nand cannot install archived mods using this GUI,\nwould you like to download 7-zip?\n(Requires an active internet connection)' 471 | errorLayout = [ [sg.Text(message, key="message")], 472 | [sg.Button('Yes'), sg.Button('No')]] 473 | case "7zSuccess": 474 | message = '7-zip successfully downloaded and installed.' 475 | case "7zFailDown": 476 | message = '7-zip was not successfully downloaded.' 477 | case "7zFailInstall": 478 | message = '7-zip was not successfully installed.' 479 | case "multiConfig": 480 | message = 'Making configs for multiplayer is currently not supported.\nHowever you can still host a multiplayer server if you manually make a config' 481 | case "custom empty": 482 | message = 'You selected other, but left the text entry empty.' 483 | case "must name config": 484 | message = 'You must set a config name.' 485 | case "prompt to download gamesettings": 486 | message = 'You currently do not have the main server configuration files that are needed for Black ops II to host properly.\nWould you like to download them? (Requires an active internet connection)' 487 | errorLayout = [ [sg.Text(message, key="message")], 488 | [sg.Button('Yes '), sg.Button('No')]] 489 | case "downloaded main configs": 490 | message = 'Black ops II main configuration files successfully downloaded.' 491 | 492 | 493 | errorWindow = sg.Window('LanLauncher', errorLayout, icon=('LAN.ico'), finalize=True) 494 | errorWindow['message'].update(message) 495 | windowLoop = True 496 | while (windowLoop == True): 497 | event, values = errorWindow.read() 498 | if event == sg.WIN_CLOSED or event == 'Close' or event == 'No': 499 | windowLoop = False 500 | errorWindow.close() 501 | elif event == 'Yes': 502 | abort = False 503 | windowLoop = False 504 | errorWindow.close() 505 | try: 506 | wget.download(r"https://raw.githubusercontent.com/JugAndDoubleTap/LanLauncher/main/7z.zip") 507 | except Exception as e: 508 | print(e) 509 | ErrorWindow(GENERAL, settings, '7zFailDown') 510 | abort = True 511 | if abort != True: 512 | try: 513 | ZipFile('7z.zip').extractall(os.getcwd()) 514 | except Exception as e: 515 | print(e) 516 | ErrorWindow(GENERAL, settings, '7zFailInstall') 517 | abort = True 518 | if abort != True: 519 | try: 520 | os.remove(os.getcwd() + "/7z.zip") 521 | except Exception as e: 522 | print(e) 523 | ErrorWindow(GENERAL, settings, '7zSuccess') 524 | elif event == 'Yes ': 525 | windowLoop = False 526 | errorWindow.close() 527 | LL.DownloadMainConfigs(GENERAL, settings) 528 | 529 | 530 | 531 | 532 | def UpdateWindow(GENERAL, settings, values): 533 | sg.theme(settings[GENERAL["THEME"]]) 534 | sg.set_options(font=('Cambria', 10)) 535 | config = configparser.ConfigParser() 536 | updatetext = 'There are no updates currently available.' 537 | update_num = r"0" 538 | try: 539 | wget.download(r"https://raw.githubusercontent.com/JugAndDoubleTap/LanLauncher/main/update.ini") 540 | config.read('update.ini') 541 | update_num = config.get('Update', 'update number') 542 | except Exception: 543 | updatetext = 'Could not check for latest version.' 544 | 545 | if settings[GENERAL["VERSIONNUM"]] < update_num: 546 | updatetext = f'An update is available,\nwould you like to update to version {update_num}?' 547 | updatewinlay = [[sg.Text(updatetext)], 548 | [sg.Button('Cancel'), sg.Button('Update')]] 549 | elif settings[GENERAL["VERSIONNUM"]] >= update_num: 550 | updatewinlay = [[sg.Text(updatetext)], 551 | [sg.Button('Close')]] 552 | 553 | update_win = sg.Window("LanLauncher", updatewinlay, icon=('LAN.ico')) 554 | mainWindow = True 555 | update = True 556 | while update == True: 557 | event, values = update_win.read() 558 | if event == 'Close': 559 | try: 560 | os.remove('update.ini') 561 | update = False 562 | update_win.close() 563 | except Exception: 564 | update = False 565 | update_win.close() 566 | elif event == sg.WIN_CLOSED or event == 'Cancel': 567 | try: 568 | os.remove('update.ini') 569 | update = False 570 | update_win.close() 571 | except Exception: 572 | update = False 573 | update_win.close() 574 | elif event == 'Update': 575 | os.remove('update.ini') 576 | LL.SaveToINI(GENERAL, settings, values) 577 | update = False 578 | update_win.close() 579 | mainWindow = False 580 | mainWindow.close() 581 | subprocess.Popen('LLUpdater.exe', shell=True) 582 | sys.exit() 583 | 584 | 585 | 586 | def MakeConfigWindow(GENERAL, settings, serverid, multiplayerMode, mainWindow): 587 | prer4516 = False 588 | if multiplayerMode == True: 589 | ErrorWindow(GENERAL, settings, "multiConfig") 590 | return 591 | 592 | 593 | match serverid: 594 | case "World at War": 595 | mapSelection = sg.Column([ 596 | [sg.Text("Map selector")], 597 | [sg.Radio("Nacht der Untoten", key="mapnacht" , group_id='mapgroup', default=True)], 598 | [sg.Radio("Verrückt", key="mapverru", group_id='mapgroup')], 599 | [sg.Radio("Shi No Numa", key="mapshi", group_id='mapgroup')], 600 | [sg.Radio("Der Riese", key="mapder", group_id='mapgroup')], 601 | [sg.Radio("Other", key="mapother", group_id='mapgroup')], 602 | [sg.Text("Input map name below for custom maps\n(Other must be selected)", font=("Cambria", 8, "bold"))], 603 | [sg.InputText("", key="mapothertext")]], vertical_alignment='top') 604 | case "Black ops": 605 | mapSelection = sg.Column([ 606 | [sg.Text("Map selector")], 607 | [sg.Radio("Kino der Toten", key="mapkino" , group_id='mapgroup', default=True)], 608 | [sg.Radio("Five", key="mapfive", group_id='mapgroup')], 609 | [sg.Radio("Ascension", key="mapasc", group_id='mapgroup')], 610 | [sg.Radio("Call of the Dead", key="mapcall", group_id='mapgroup')], 611 | [sg.Radio("Shangri-La", key="mapcall", group_id='mapgroup')], 612 | [sg.Radio("Moon", key="mapcall", group_id='mapgroup')], 613 | [sg.Radio("Dead ops Arcade", key="mapops", group_id='mapgroup')], 614 | [sg.Radio("Nacht der Untoten", key="mapnacht" , group_id='mapgroup')], 615 | [sg.Radio("Verrückt", key="mapverru", group_id='mapgroup')], 616 | [sg.Radio("Shi No Numa", key="mapshi", group_id='mapgroup')], 617 | [sg.Radio("Der Riese", key="mapder", group_id='mapgroup')]], vertical_alignment='top') 618 | case "Black ops II": 619 | mapSelection = [ 620 | sg.Column([ 621 | #[sg.Text("Pre r4516")], 622 | #[sg.Radio("Yes", key="prer4516", group_id="r4516")], 623 | #[sg.Radio("No", group_id="r4516", default=True)], 624 | [sg.Text("Map selector")], 625 | [sg.Radio("Tranzit", key="mapbus" , group_id='mapgroup', default=True)], 626 | [sg.Radio("Nuke Town", key="mapnuke", group_id='mapgroup')], 627 | [sg.Radio("Die Rise", key="maprise", group_id='mapgroup')], 628 | [sg.Radio("Mob of the Dead", key="mapmob", group_id='mapgroup')], 629 | [sg.Radio("Buried", key="mapburied", group_id='mapgroup')], 630 | [sg.Radio("Origins", key="maporigins", group_id='mapgroup')]], vertical_alignment='top'), 631 | 632 | sg.Column([ 633 | [sg.Text(" ")], 634 | [sg.Radio("Farm Survival", key="mapfarm", group_id='mapgroup')], 635 | [sg.Radio("Town Survival", key="maptown", group_id='mapgroup')], 636 | [sg.Radio("Bus Depot Survival", key="mapdepot", group_id='mapgroup')]], vertical_alignment='bottom'), 637 | ] 638 | 639 | 640 | if not os.path.exists(settings[GENERAL["PLUTONIUMINSTANCE"]] + '/storage/t6/gamesettings'): 641 | ErrorWindow(GENERAL, settings, "prompt to download gamesettings") 642 | return 643 | case "Modern Warfare 3": 644 | mapSelection = [] 645 | ErrorWindow(GENERAL, settings, "multiConfig") 646 | return 647 | case _: 648 | return 649 | 650 | 651 | cofigLayout = [ 652 | 653 | [ 654 | 655 | mapSelection, 656 | 657 | ], 658 | 659 | [sg.Column([ [sg.Text("")], 660 | [sg.Text("Name of config without suffix or prefix\nE.G.: DieRise", font=("Cambria", 8, "bold"))], 661 | [sg.InputText("", key="configname")], 662 | [sg.Text("")]]),], 663 | 664 | [sg.Button('Close'), sg.Button("Make config")]] 665 | cofigWindow = sg.Window('LanLauncher', cofigLayout, icon=('LAN.ico'), finalize=True) 666 | windowLoop = True 667 | while (windowLoop == True): 668 | event, values = cofigWindow.read() 669 | match event: 670 | case sg.WIN_CLOSED | 'Close': 671 | windowLoop = False 672 | cofigWindow.close() 673 | case 'Make config': 674 | abort = False 675 | 676 | if values["configname"] == '': 677 | ErrorWindow(GENERAL, settings, "must name config") 678 | abort = True 679 | 680 | 681 | 682 | match serverid: 683 | case "World at War": 684 | if values["mapnacht"]: 685 | selectedMap = "map nazi_zombie_prototype" 686 | elif values["mapverru"]: 687 | selectedMap = "map nazi_zombie_asylum" 688 | elif values["mapshi"]: 689 | selectedMap = "map nazi_zombie_sumpf" 690 | elif values["mapder"]: 691 | selectedMap = "map nazi_zombie_factory" 692 | elif values["mapother"]: 693 | if values["mapothertext"] == '': 694 | ErrorWindow(GENERAL, settings, "custom empty") 695 | abort = True 696 | else: 697 | selectedMap = "map " + values["mapothertext"] 698 | 699 | case "Black ops": 700 | pass 701 | 702 | case "Black ops II": 703 | #temp solution to make prer4516 never true, as I do not think that I want it included in final release 704 | if 1==3: #values["prer4516"]: 705 | prer4516 = True 706 | if values["mapbus"]: 707 | selectedMap = "exec zm_classic_transit.cfg map zm_transit" 708 | elif values["mapfarm"]: 709 | selectedMap = "exec zm_standard_farm map zm_transit" 710 | elif values["maptown"]: 711 | selectedMap = "exec zm_standard_town.cfg map zm_transit" 712 | elif values["mapdepot"]: 713 | selectedMap = "exec zm_standard_transit.cfg map zm_transit" 714 | elif values["mapnuke"]: 715 | selectedMap = "exec zm_standard_nuked.cfg map zm_nuked" 716 | elif values["maprise"]: 717 | selectedMap = "exec zm_classic_rooftop.cfg map zm_highrise" 718 | elif values["mapmob"]: 719 | selectedMap = "exec zm_classic_prison.cfg map zm_prison" 720 | elif values["mapburied"]: 721 | selectedMap = "exec zm_classic_processing.cfg map zm_buried" 722 | elif values["maporigins"]: 723 | selectedMap = "exec zm_classic_tomb.cfg map zm_tomb" 724 | else: 725 | if values["mapbus"]: 726 | selectedMap = "execgts zm_classic_transit.cfg map zm_transit" 727 | elif values["mapfarm"]: 728 | selectedMap = "execgts zm_standard_farm map zm_transit" 729 | elif values["maptown"]: 730 | selectedMap = "execgts zm_standard_town.cfg map zm_transit" 731 | elif values["mapdepot"]: 732 | selectedMap = "execgts zm_standard_transit.cfg map zm_transit" 733 | elif values["mapnuke"]: 734 | selectedMap = "execgts zm_standard_nuked.cfg map zm_nuked" 735 | elif values["maprise"]: 736 | selectedMap = "execgts zm_classic_rooftop.cfg map zm_highrise" 737 | elif values["mapmob"]: 738 | selectedMap = "execgts zm_classic_prison.cfg map zm_prison" 739 | elif values["mapburied"]: 740 | selectedMap = "execgts zm_classic_processing.cfg map zm_buried" 741 | elif values["maporigins"]: 742 | selectedMap = "execgts zm_classic_tomb.cfg map zm_tomb" 743 | 744 | case "Modern Warfare 3": 745 | pass 746 | 747 | if abort != True: 748 | LLC.GenerateConfig(GENERAL, settings, serverid, values["configname"], selectedMap, multiplayerMode, prer4516) 749 | 750 | match serverid: 751 | case "World at War": 752 | path = settings[GENERAL["PLUTONIUMINSTANCE"]] + fr'/storage/t4/main' 753 | case "Black ops": 754 | path = settings[GENERAL["PLUTONIUMINSTANCE"]] + fr'/storage/t5/main' 755 | case "Black ops II": 756 | path = settings[GENERAL["PLUTONIUMINSTANCE"]] + fr'/storage/t6/main' 757 | case "Modern Warfare 3": 758 | path = settings[GENERAL["PLUTONIUMINSTANCE"]] + fr'/storage/iw5/main' 759 | 760 | configList = [] 761 | mainWindow['configlist'].update(values=configList) 762 | 763 | if os.path.exists(path): 764 | configList = [f for f in os.listdir(path) if f.endswith('.cfg')] 765 | mainWindow['configlist'].update(values=configList) 766 | 767 | windowLoop = False 768 | cofigWindow.close() 769 | -------------------------------------------------------------------------------- /LanLauncher.pyw: -------------------------------------------------------------------------------- 1 | import subprocess, os, shutil, argparse, configparser, glob, wget 2 | import LLWindows as LLW 3 | import LLGameStarter as LLG 4 | 5 | def main(): 6 | # sets up argument parser for commandline argument usage 7 | parser = argparse.ArgumentParser(description='LanLauncher, for when you want to launch plutonium without an active internet connection!') 8 | parser.add_argument('-name', type=str, help='username that you will use example: "JugAndDoubleTap", default is the one you set in the GUI', default=None) 9 | parser.add_argument('-plutoniumdir', type=str, help='location at which your plutonium folder is stored example: "C:/user/(name)/appdata/local/plutonium", by default uses the one that was set in the GUI, Must be used with -nogui', default=None) 10 | parser.add_argument('-mode', type=str, help='mode selector, either input "MP" or "ZM", MUST be used with -nogui', default="ZM") 11 | parser.add_argument('-gamedir', type=str, help='location at which your game is stored example: "C:/games/cod world at war", by default uses the one that was set in the GUI, MUST be used with -nogui', default=None) 12 | parser.add_argument('-gameid', type=str, help='accepts game id to set the game, such as T4, T5, T6, IW5, MUST be used with -nogui', default=None) 13 | parser.add_argument('-nogui', action='store_true', help='launches with no gui') 14 | args = parser.parse_args() 15 | 16 | # dictionary of all settings variable names, to use as enums for array indexing 17 | GENERAL = { 18 | "VERSIONNUM" : 0, 19 | "THEME" : 1, 20 | "USERNAME" : 2, 21 | "PLUTONIUMINSTANCE" : 3, 22 | "WAW" : 4, 23 | "BO1" : 5, 24 | "BO2" : 6, 25 | "MW3" : 7, 26 | "MODID" : 8, 27 | "GAMEID" : 9, 28 | "SERVERID" : 10, 29 | "MODEID" : 11, 30 | "NOGUI" : 12, 31 | "WAWSERVMULT" : 13, 32 | "ACTIVEGAME" : 14 33 | } 34 | # initilizes array size based on dictionary, and sets all indexes to '' (empty) to start off 35 | settings = [''] * len(GENERAL) 36 | 37 | # assigning items to array index via dictionary example for me 38 | #settingsArray[GENERAL["WAW"]] = "/games/general/waw/" 39 | #print(settingsArray[GENERAL["WAW"]]) 40 | # output : /games/general/waw/ 41 | 42 | # sets the version number 43 | settings[GENERAL["VERSIONNUM"]] = "2.0.0" 44 | # sets default theme 45 | settings[GENERAL["THEME"]] = "DarkAmber" 46 | 47 | 48 | if os.path.isfile("LanLauncher.ini"): 49 | # load cfg function 50 | LoadFromINI(GENERAL, settings) 51 | 52 | # this is only used when PLUTONIUMINSTANCE is not set, such as when running for the first time, or if it is deleted from the ini 53 | if settings[GENERAL["PLUTONIUMINSTANCE"]] == '': 54 | # sets the plutonium instance to the default installation location for plutonium if PLUTONIUMINSTANCE is Null 55 | if os.path.exists(r"C:/Users/" + os.getlogin() + r"/AppData/Local/Plutonium"): 56 | settings[GENERAL["PLUTONIUMINSTANCE"]] = r"C:/Users/" + os.getlogin() + r"/AppData/Local/Plutonium" 57 | 58 | if args.nogui == False: 59 | # normal launch 60 | LLW.MainWindow(GENERAL, settings) 61 | else: 62 | # no gui launch 63 | settings[GENERAL["NOGUI"]] = "True" 64 | 65 | # if a custom location to a plutonium instance is provided in the cmd args then this is used 66 | if args.plutoniumdir != None: 67 | settings[GENERAL["PLUTONIUMINSTANCE"]] = args.plutoniumdir 68 | # if a name is provided using commandline arguments then this is used 69 | if args.name != None: 70 | settings[GENERAL["USERNAME"]] = args.name 71 | match args.gameid.upper(): 72 | case "T4": 73 | if args.mode.upper() == "MP": 74 | settings[GENERAL["MODEID"]] = "t4mp" 75 | else: 76 | settings[GENERAL["MODEID"]] = "t4sp" 77 | 78 | settings[GENERAL["GAMEID"]] = "World at War" 79 | # sets gamedir if a custom location is provdied 80 | if args.gamedir != None: 81 | settings[GENERAL["WAW"]] = args.gamedir 82 | LLG.Launch(GENERAL, settings, '') 83 | case "T5": 84 | if args.mode.upper() == "MP": 85 | settings[GENERAL["MODEID"]] = "t5mp" 86 | else: 87 | settings[GENERAL["MODEID"]] = "t5sp" 88 | 89 | settings[GENERAL["GAMEID"]] = "Black ops" 90 | # sets gamedir if a custom location is provdied 91 | if args.gamedir != None: 92 | settings[GENERAL["BO1"]] = args.gamedir 93 | LLG.Launch(GENERAL, settings, '') 94 | case "T6": 95 | if args.mode.upper() == "MP": 96 | settings[GENERAL["MODEID"]] = "t6mp" 97 | else: 98 | settings[GENERAL["MODEID"]] = "t6zm" 99 | 100 | settings[GENERAL["GAMEID"]] = "Black ops II" 101 | # sets gamedir if a custom location is provdied 102 | if args.gamedir != None: 103 | settings[GENERAL["BO2"]] = args.gamedir 104 | LLG.Launch(GENERAL, settings, '') 105 | case "IW5": 106 | settings[GENERAL["MODEID"]] = "iw5mp" 107 | settings[GENERAL["GAMEID"]] = "Modern Warfare 3" 108 | # sets gamedir if a custom location is provdied 109 | if args.gamedir != None: 110 | settings[GENERAL["MW3"]] = args.gamedir 111 | LLG.Launch(GENERAL, settings, '') 112 | case _: 113 | print("No valid game selected.") 114 | 115 | 116 | def SaveToINI(GENERAL, settings, values): 117 | config = configparser.ConfigParser() 118 | 119 | if not os.path.isfile("LanLauncher.ini"): 120 | config['LanLauncher'] = {'username': str(values["username"]), 121 | 'plutonium folder': str(values["plutoniuminstance"]), 122 | 'world at war folder': str(values["waw"]), 123 | 'black ops 1 folder': str(values["bo1"]), 124 | 'black ops 2 folder': str(values["bo2"]), 125 | 'modernwarfare 3 folder': str(values["mw3"]), 126 | 'theme': settings[GENERAL["THEME"]]} 127 | with open('LanLauncher.ini', 'w') as INIFILE: 128 | config.write(INIFILE) 129 | else: 130 | config.read('LanLauncher.ini') 131 | config.set('LanLauncher', 'username', str(values["username"])) 132 | config.set('LanLauncher', 'world at war folder', str(values["waw"])) 133 | config.set('LanLauncher', 'black ops 1 folder', str(values["bo1"])) 134 | config.set('LanLauncher', 'black ops 2 folder', str(values["bo2"])) 135 | config.set('LanLauncher', 'modernwarfare 3 folder', str(values["mw3"])) 136 | config.set('LanLauncher', 'plutonium folder', str(values["plutoniuminstance"])) 137 | config.set('LanLauncher', 'theme', settings[GENERAL["THEME"]]) 138 | with open('LanLauncher.ini', 'w') as INIFILE: 139 | config.write(INIFILE) 140 | 141 | def LoadFromINI(GENERAL, settings): 142 | config = configparser.ConfigParser() 143 | 144 | config.read('LanLauncher.ini') 145 | try: 146 | settings[GENERAL["USERNAME"]] = config.get('LanLauncher', 'username') 147 | except Exception as e: 148 | print(e) 149 | config.set('LanLauncher', 'username', settings[GENERAL["USERNAME"]]) 150 | try: 151 | settings[GENERAL["PLUTONIUMINSTANCE"]] = config.get('LanLauncher', 'plutonium folder') 152 | except Exception as e: 153 | print(e) 154 | config.set('LanLauncher', 'plutonium folder', settings[GENERAL["PLUTONIUMINSTANCE"]]) 155 | try: 156 | settings[GENERAL["WAW"]] = config.get('LanLauncher', 'world at war folder') 157 | except Exception as e: 158 | print(e) 159 | config.set('LanLauncher', 'world at war folder', settings[GENERAL["WAW"]]) 160 | try: 161 | settings[GENERAL["BO1"]] = config.get('LanLauncher', 'black ops 1 folder') 162 | except Exception as e: 163 | print(e) 164 | config.set('LanLauncher', 'black ops 1 folder', settings[GENERAL["BO1"]]) 165 | try: 166 | settings[GENERAL["BO2"]] = config.get('LanLauncher', 'black ops 2 folder') 167 | except Exception as e: 168 | print(e) 169 | config.set('LanLauncher', 'black ops 2 folder', settings[GENERAL["BO2"]]) 170 | try: 171 | settings[GENERAL["MW3"]] = config.get('LanLauncher', 'modernwarfare 3 folder') 172 | except Exception as e: 173 | print(e) 174 | config.set('LanLauncher', 'modernwarfare 3 folder', settings[GENERAL["MW3"]]) 175 | try: 176 | settings[GENERAL["THEME"]] = config.get('LanLauncher', 'theme') 177 | except Exception as e: 178 | print(e) 179 | config.set('LanLauncher', 'theme', settings[GENERAL["THEME"]]) 180 | 181 | 182 | def ExtractArchive(modfolder, mapexe): 183 | fourLetterExtension = mapexe[-4:] 184 | threeLetterExtension = mapexe[-3:] 185 | 186 | 187 | if threeLetterExtension.lower() == ".7z": 188 | fileName = mapexe[:-3] 189 | else: 190 | match fourLetterExtension.lower(): 191 | case ".zip": 192 | fileName = os.path.basename(mapexe)[:-4] 193 | case ".rar": 194 | fileName = os.path.basename(mapexe)[:-4] 195 | case ".exe": 196 | fileName = os.path.basename(mapexe)[:-4] 197 | case _: 198 | return "not supported extension" 199 | try: 200 | mapexecon = f'"{mapexe}"' 201 | modfoldercon = f'"{modfolder}"' 202 | modfoldercontemp = f'"{modfolder}/TEMP"' 203 | modfolderFileName = os.path.join(modfolder, fileName.replace(" ", "_")) 204 | sevz = rf'"{os.getcwd()}\7z\7z.exe"' 205 | 206 | 207 | if not os.path.exists(modfolder + "/TEMP"): 208 | os.mkdir(modfolder + "/TEMP") 209 | else: 210 | shutil.rmtree(modfolder + "/TEMP") 211 | os.mkdir(modfolder + "/TEMP") 212 | 213 | 214 | subprocess.Popen(fr'{sevz} x -y -o{modfoldercontemp} {mapexecon}', creationflags=0x00000010, shell=True ,cwd=os.getcwd() + r"/7z").wait() 215 | 216 | if os.path.isfile(modfolder + "/TEMP/mod.ff") or os.path.isdir(modfolder + "/TEMP/scripts") or os.path.isdir(modfolder + "/TEMP/images") or os.path.isdir(modfolder + "/TEMP/maps"): 217 | if glob.glob(os.path.join(modfolder, "TEMP", "*.lua")) or glob.glob(os.path.join(modfolder, "TEMP", "*.gsc")): 218 | shutil.rmtree(modfolder + "/TEMP") 219 | return "not standard mod format" 220 | 221 | shutil.rmtree(modfolder + "/TEMP") 222 | 223 | os.mkdir(modfolderFileName) 224 | subprocess.Popen(fr'{sevz} x -y -o"{modfolderFileName}" {mapexecon}', creationflags=0x00000010, shell=True ,cwd=os.getcwd() + r"/7z").wait() 225 | 226 | if os.path.exists(modfolder + '/$PLUGINSDIR'): 227 | shutil.rmtree(modfolder + '/$PLUGINSDIR') 228 | else: 229 | if glob.glob(os.path.join(modfolder, "TEMP", "*", "*.lua")) or glob.glob(os.path.join(modfolder, "TEMP", "*", "*.gsc")): 230 | shutil.rmtree(modfolder + "/TEMP") 231 | return "not standard mod format" 232 | 233 | shutil.rmtree(modfolder + "/TEMP") 234 | subprocess.Popen(fr'{sevz} x -y -o{modfoldercon} {mapexecon}', creationflags=0x00000010, shell=True ,cwd=os.getcwd() + r"/7z").wait() 235 | if os.path.exists(modfolder + '/$PLUGINSDIR'): 236 | shutil.rmtree(modfolder + '/$PLUGINSDIR') 237 | 238 | except Exception as e: 239 | print(e) 240 | return "failed to install mod" 241 | 242 | return "standard mod format" 243 | 244 | 245 | 246 | 247 | def DownloadMainConfigs(GENERAL, settings): 248 | downloadList = [ 249 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/conf.cfg", 250 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/ctf.cfg", 251 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/dem.cfg", 252 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/dm.cfg", 253 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/dom.cfg", 254 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/gun.cfg", 255 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/hq.cfg", 256 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/koth.cfg", 257 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/oic.cfg", 258 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/oneflag.cfg", 259 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/sas.cfg", 260 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/sd.cfg", 261 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/shrp.cfg", 262 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/tdm.cfg", 263 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_classic_prison.cfg", 264 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_classic_processing.cfg", 265 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_classic_rooftop.cfg", 266 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_classic_tomb.cfg", 267 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_classic_transit.cfg", 268 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_cleansed_diner.cfg", 269 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_cleansed_street.cfg", 270 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_grief_cellblock.cfg", 271 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_grief_farm.cfg", 272 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_grief_street.cfg", 273 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_grief_town.cfg", 274 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_grief_transit.cfg", 275 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_standard_farm.cfg", 276 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_standard_nuked.cfg", 277 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_standard_town.cfg", 278 | r"https://raw.githubusercontent.com/xerxes-at/T6ServerConfigs/refs/heads/master/localappdata/Plutonium/storage/t6/gamesettings/zm_standard_transit.cfg"] 279 | 280 | 281 | destinationList = [ 282 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/conf.cfg", 283 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/ctf.cfg", 284 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/dem.cfg", 285 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/dm.cfg", 286 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/dom.cfg", 287 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/gun.cfg", 288 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/hq.cfg", 289 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/koth.cfg", 290 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/oic.cfg", 291 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/oneflag.cfg", 292 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/sas.cfg", 293 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/sd.cfg", 294 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/shrp.cfg", 295 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/tdm.cfg", 296 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_classic_prison.cfg", 297 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_classic_processing.cfg", 298 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_classic_rooftop.cfg", 299 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_classic_tomb.cfg", 300 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_classic_transit.cfg", 301 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_cleansed_diner.cfg", 302 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_cleansed_street.cfg", 303 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_grief_cellblock.cfg", 304 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_grief_farm.cfg", 305 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_grief_street.cfg", 306 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_grief_town.cfg", 307 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_grief_transit.cfg", 308 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_standard_farm.cfg", 309 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_standard_nuked.cfg", 310 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_standard_town.cfg", 311 | fr"{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings/zm_standard_transit.cfg"] 312 | try: 313 | os.mkdir(fr'{settings[GENERAL["PLUTONIUMINSTANCE"]]}/storage/t6/gamesettings') 314 | downloadItems = list(zip(downloadList, destinationList)) 315 | for downloadUrl, destinationPath in downloadItems: 316 | wget.download(downloadUrl, destinationPath) 317 | LLW.ErrorWindow(GENERAL, settings, "downloaded main configs") 318 | except Exception as e: 319 | print(e) 320 | 321 | if __name__ == "__main__": 322 | main() -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | 2 | # LanLauncher 3 | 4 | ## Overview 5 | 6 | Ever wanted to play **Plutonium** without an internet connection? This project, **LanLauncher**, allows you to launch **Plutonium** without requiring an internet connection, ensuring a hassle-free experience. It works on **Windows**, and **Linux** systems (such as the **Steam Deck**). (wine is required for linux systems) 7 | 8 | Just fill out the relevant information in the provided fields and click the launch button to start. 9 | 10 | ![Capture](https://github.com/JugAndDoubleTap/LanLauncher/assets/133887820/5e63b9fa-ba83-41af-8fef-3807dbc63bb8) 11 | 12 | ## Features 13 | 14 | - Launch **Plutonium** offline without an active internet connection 15 | - Launch a LAN server directly from the GUI 16 | - Install mods and custom maps directly from the GUI (must have "standard" mod file structure, like WAW custom maps) 17 | - Commandline arguments to launch with no gui, directly into a game 18 | - Compatible with **Windows** and **Linux** (with wine) 19 | - Simple user interface 20 | 21 | ## Installation 22 | 23 | 1. **Download** the latest release of LanLauncher from the [releases section.](https://github.com/JugAndDoubleTap/LanLauncher/releases) 24 | 2. **Run** the executable. No installation is required just extract the zip file, open the program and fill in the necessary paths, and launch **Plutonium** offline to your heart's content. 25 | 26 | ## Using with commandline 27 | 28 | **LanLauncher** can be launched using the following commandline arguments 29 | - **-nogui** required for all of the other arguments to work, launches with no gui using the specificed folders and gameid to launch a game, uses the settings set in the GUI if no replacements are provided (launching using -nogui -gameid "T6" -mode "ZM" and nothing else will launch Black ops 2 into zombies mode, provided you have set the path to Black ops 2 inside the GUI already) 30 | - **-mode** sets the mode, there are only 2 valid options which are "ZM" or "MP" usage example : -mode "ZM" 31 | - **-gameid** sets which game will be launched, valid options are "T4", "T5", "T6", "IW5" usage example : -gameid "T6" 32 | - **-gamedir** sets the game directory that will be launched usage example : -gamedir "Z:\SteamLibrary\steamapps\common\Call of Duty World at War" 33 | - **-plutoniumdir** sets the plutonium directory that will be used usage example : -plutoniumdir "C:\Users\User\AppData\Local\Plutonium" 34 | - **-name** sets the name that will be used ingame, default is the one set in the GUI usage example : -name "JugAndDoubleTap" 35 | 36 | 37 | 38 | This program uses the following libraries and external tools: 39 | 40 | ### Third-Party Libraries 41 | - **PyInstaller**: Used to package the Python scripts into an executable. 42 | - **FreeSimpleGUI**: Used for building the user interface. 43 | - **wget (python)**: Used mainly in the updater to download a later release. 44 | - **7-Zip**: Used for extracting mod archives. (zip, 7z, rar, exe) 45 | 46 | 47 | ## License Summary 48 | 49 | ### PyInstaller 50 | - **License**: Dual-licensed under the GNU General Public License v2.0 (GPLv2) with an exception for bundling and the Apache License 2.0 for certain files. 51 | - **Usage**: PyInstaller is used to package the Python scripts into an executable format. 52 | - **Source**: [PyInstaller GitHub Repository](https://github.com/pyinstaller/pyinstaller) 53 | - **License Text**: A copy of the PyInstaller license is included [here.](https://github.com/JugAndDoubleTap/LanLauncher/raw/main/ThirdParty-Licenses/pyinstaller-license.txt) 54 | 55 | ### 7-Zip 56 | - **License**: GNU Lesser General Public License v2.1 (LGPLv2.1), with an "unRAR license restriction" and BSD 3-Clause License for specific parts. 57 | - **Usage**: 7-Zip is used to extract mod archives. 58 | - **Source**: [7-Zip Website](https://www.7-zip.org) 59 | - **License Text**: A copy of the 7-Zip license is included [here.](https://github.com/JugAndDoubleTap/LanLauncher/raw/main/ThirdParty-Licenses/7zip-license.txt) 60 | 61 | ### Original Code License 62 | This project, **LanLauncher**, is licensed under the **GNU Lesser General Public License v3.0 (LGPLv3)**. A copy of this license is provided [here.](https://github.com/JugAndDoubleTap/LanLauncher/raw/main/LICENSE) 63 | 64 | 65 | ### Additional Information 66 | 67 | [Plutonium launcher website.](https://plutonium.pw/) 68 | 69 | For more details about the licenses, see the `ThirdParty-Licenses` directory in this repository, where the full text of relevant third-party licenses is included. 70 | -------------------------------------------------------------------------------- /ThirdParty-Licenses/7zip-license.txt: -------------------------------------------------------------------------------- 1 | 7-Zip 2 | ~~~~~ 3 | License for use and distribution 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | 7-Zip Copyright (C) 1999-2023 Igor Pavlov. 7 | 8 | The licenses for files are: 9 | 10 | 1) 7z.dll: 11 | - The "GNU LGPL" as main license for most of the code 12 | - The "GNU LGPL" with "unRAR license restriction" for some code 13 | - The "BSD 3-clause License" for some code 14 | 2) All other files: the "GNU LGPL". 15 | 16 | Redistributions in binary form must reproduce related license information from this file. 17 | 18 | Note: 19 | You can use 7-Zip on any computer, including a computer in a commercial 20 | organization. You don't need to register or pay for 7-Zip. 21 | 22 | 23 | GNU LGPL information 24 | -------------------- 25 | 26 | This library is free software; you can redistribute it and/or 27 | modify it under the terms of the GNU Lesser General Public 28 | License as published by the Free Software Foundation; either 29 | version 2.1 of the License, or (at your option) any later version. 30 | 31 | This library is distributed in the hope that it will be useful, 32 | but WITHOUT ANY WARRANTY; without even the implied warranty of 33 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 34 | Lesser General Public License for more details. 35 | 36 | You can receive a copy of the GNU Lesser General Public License from 37 | http://www.gnu.org/ 38 | 39 | 40 | 41 | 42 | BSD 3-clause License 43 | -------------------- 44 | 45 | The "BSD 3-clause License" is used for the code in 7z.dll that implements LZFSE data decompression. 46 | That code was derived from the code in the "LZFSE compression library" developed by Apple Inc, 47 | that also uses the "BSD 3-clause License": 48 | 49 | ---- 50 | Copyright (c) 2015-2016, Apple Inc. All rights reserved. 51 | 52 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 53 | 54 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 55 | 56 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 57 | in the documentation and/or other materials provided with the distribution. 58 | 59 | 3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived 60 | from this software without specific prior written permission. 61 | 62 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 63 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 64 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 65 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 67 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 68 | ---- 69 | 70 | 71 | 72 | 73 | unRAR license restriction 74 | ------------------------- 75 | 76 | The decompression engine for RAR archives was developed using source 77 | code of unRAR program. 78 | All copyrights to original unRAR code are owned by Alexander Roshal. 79 | 80 | The license for original unRAR code has the following restriction: 81 | 82 | The unRAR sources cannot be used to re-create the RAR compression algorithm, 83 | which is proprietary. Distribution of modified unRAR sources in separate form 84 | or as a part of other software is permitted, provided that it is clearly 85 | stated in the documentation and source comments that the code may 86 | not be used to develop a RAR (WinRAR) compatible archiver. 87 | 88 | 89 | -- 90 | Igor Pavlov -------------------------------------------------------------------------------- /ThirdParty-Licenses/pyinstaller-license.txt: -------------------------------------------------------------------------------- 1 | ================================ 2 | The PyInstaller licensing terms 3 | ================================ 4 | 5 | 6 | Copyright (c) 2010-2023, PyInstaller Development Team 7 | Copyright (c) 2005-2009, Giovanni Bajo 8 | Based on previous work under copyright (c) 2002 McMillan Enterprises, Inc. 9 | 10 | 11 | PyInstaller is licensed under the terms of the GNU General Public License 12 | as published by the Free Software Foundation; either version 2 of the License, 13 | or (at your option) any later version. 14 | 15 | 16 | Bootloader Exception 17 | -------------------- 18 | 19 | In addition to the permissions in the GNU General Public License, the 20 | authors give you unlimited permission to link or embed compiled bootloader 21 | and related files into combinations with other programs, and to distribute 22 | those combinations without any restriction coming from the use of those 23 | files. (The General Public License restrictions do apply in other respects; 24 | for example, they cover modification of the files, and distribution when 25 | not linked into a combined executable.) 26 | 27 | 28 | Bootloader and Related Files 29 | ---------------------------- 30 | 31 | Bootloader and related files are files which are embedded within the 32 | final executable. This includes files in directories: 33 | 34 | ./bootloader/ 35 | ./PyInstaller/loader 36 | 37 | 38 | Run-time Hooks 39 | ---------------------------- 40 | 41 | Run-time Hooks are a different kind of files embedded within the final 42 | executable. To ease moving them into a separate repository, or into the 43 | respective project, these files are now licensed under the Apache License, 44 | Version 2.0. 45 | 46 | Run-time Hooks are in the directory 47 | ./PyInstaller/hooks/rthooks 48 | 49 | 50 | The PyInstaller.isolated submodule 51 | ---------------------------------- 52 | 53 | By request, the PyInstaller.isolated submodule and its corresponding tests are 54 | additionally licensed with the MIT license so that it may be reused outside of 55 | PyInstaller under GPL 2.0 or MIT terms and conditions -- whichever is the most 56 | suitable to the recipient downstream project. Affected files/directories are: 57 | 58 | ./PyInstaller/isolated/ 59 | ./tests/unit/test_isolation.py 60 | 61 | 62 | About the PyInstaller Development Team 63 | -------------------------------------- 64 | 65 | The PyInstaller Development Team is the set of contributors 66 | to the PyInstaller project. A full list with details is kept 67 | in the documentation directory, in the file 68 | ``doc/CREDITS.rst``. 69 | 70 | The core team that coordinates development on GitHub can be found here: 71 | https://github.com/pyinstaller/pyinstaller. As of 2021, it consists of: 72 | 73 | * Hartmut Goebel 74 | * Jasper Harrison 75 | * Bryan Jones 76 | * Brenainn Woodsend 77 | * Rok Mandeljc 78 | 79 | Our Copyright Policy 80 | -------------------- 81 | 82 | PyInstaller uses a shared copyright model. Each contributor maintains copyright 83 | over their contributions to PyInstaller. But, it is important to note that these 84 | contributions are typically only changes to the repositories. Thus, 85 | the PyInstaller source code, in its entirety is not the copyright of any single 86 | person or institution. Instead, it is the collective copyright of the entire 87 | PyInstaller Development Team. If individual contributors want to maintain 88 | a record of what changes/contributions they have specific copyright on, they 89 | should indicate their copyright in the commit message of the change, when they 90 | commit the change to the PyInstaller repository. 91 | 92 | With this in mind, the following banner should be used in any source code file 93 | to indicate the copyright and license terms: 94 | 95 | 96 | #----------------------------------------------------------------------------- 97 | # Copyright (c) 2005-2023, PyInstaller Development Team. 98 | # 99 | # Distributed under the terms of the GNU General Public License (version 2 100 | # or later) with exception for distributing the bootloader. 101 | # 102 | # The full license is in the file COPYING.txt, distributed with this software. 103 | # 104 | # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception) 105 | #----------------------------------------------------------------------------- 106 | 107 | 108 | For run-time hooks, the following banner should be used: 109 | 110 | #----------------------------------------------------------------------------- 111 | # Copyright (c) 2005-2023, PyInstaller Development Team. 112 | # 113 | # Licensed under the Apache License, Version 2.0 (the "License"); 114 | # you may not use this file except in compliance with the License. 115 | # 116 | # The full license is in the file COPYING.txt, distributed with this software. 117 | # 118 | # SPDX-License-Identifier: Apache-2.0 119 | #----------------------------------------------------------------------------- 120 | 121 | 122 | ================================ 123 | GNU General Public License 124 | ================================ 125 | 126 | https://gnu.org/licenses/gpl-2.0.html 127 | 128 | 129 | GNU GENERAL PUBLIC LICENSE 130 | Version 2, June 1991 131 | 132 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 133 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 134 | Everyone is permitted to copy and distribute verbatim copies 135 | of this license document, but changing it is not allowed. 136 | 137 | Preamble 138 | 139 | The licenses for most software are designed to take away your 140 | freedom to share and change it. By contrast, the GNU General Public 141 | License is intended to guarantee your freedom to share and change free 142 | software--to make sure the software is free for all its users. This 143 | General Public License applies to most of the Free Software 144 | Foundation's software and to any other program whose authors commit to 145 | using it. (Some other Free Software Foundation software is covered by 146 | the GNU Library General Public License instead.) You can apply it to 147 | your programs, too. 148 | 149 | When we speak of free software, we are referring to freedom, not 150 | price. Our General Public Licenses are designed to make sure that you 151 | have the freedom to distribute copies of free software (and charge for 152 | this service if you wish), that you receive source code or can get it 153 | if you want it, that you can change the software or use pieces of it 154 | in new free programs; and that you know you can do these things. 155 | 156 | To protect your rights, we need to make restrictions that forbid 157 | anyone to deny you these rights or to ask you to surrender the rights. 158 | These restrictions translate to certain responsibilities for you if you 159 | distribute copies of the software, or if you modify it. 160 | 161 | For example, if you distribute copies of such a program, whether 162 | gratis or for a fee, you must give the recipients all the rights that 163 | you have. You must make sure that they, too, receive or can get the 164 | source code. And you must show them these terms so they know their 165 | rights. 166 | 167 | We protect your rights with two steps: (1) copyright the software, and 168 | (2) offer you this license which gives you legal permission to copy, 169 | distribute and/or modify the software. 170 | 171 | Also, for each author's protection and ours, we want to make certain 172 | that everyone understands that there is no warranty for this free 173 | software. If the software is modified by someone else and passed on, we 174 | want its recipients to know that what they have is not the original, so 175 | that any problems introduced by others will not reflect on the original 176 | authors' reputations. 177 | 178 | Finally, any free program is threatened constantly by software 179 | patents. We wish to avoid the danger that redistributors of a free 180 | program will individually obtain patent licenses, in effect making the 181 | program proprietary. To prevent this, we have made it clear that any 182 | patent must be licensed for everyone's free use or not licensed at all. 183 | 184 | The precise terms and conditions for copying, distribution and 185 | modification follow. 186 | 187 | GNU GENERAL PUBLIC LICENSE 188 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 189 | 190 | 0. This License applies to any program or other work which contains 191 | a notice placed by the copyright holder saying it may be distributed 192 | under the terms of this General Public License. The "Program", below, 193 | refers to any such program or work, and a "work based on the Program" 194 | means either the Program or any derivative work under copyright law: 195 | that is to say, a work containing the Program or a portion of it, 196 | either verbatim or with modifications and/or translated into another 197 | language. (Hereinafter, translation is included without limitation in 198 | the term "modification".) Each licensee is addressed as "you". 199 | 200 | Activities other than copying, distribution and modification are not 201 | covered by this License; they are outside its scope. The act of 202 | running the Program is not restricted, and the output from the Program 203 | is covered only if its contents constitute a work based on the 204 | Program (independent of having been made by running the Program). 205 | Whether that is true depends on what the Program does. 206 | 207 | 1. You may copy and distribute verbatim copies of the Program's 208 | source code as you receive it, in any medium, provided that you 209 | conspicuously and appropriately publish on each copy an appropriate 210 | copyright notice and disclaimer of warranty; keep intact all the 211 | notices that refer to this License and to the absence of any warranty; 212 | and give any other recipients of the Program a copy of this License 213 | along with the Program. 214 | 215 | You may charge a fee for the physical act of transferring a copy, and 216 | you may at your option offer warranty protection in exchange for a fee. 217 | 218 | 2. You may modify your copy or copies of the Program or any portion 219 | of it, thus forming a work based on the Program, and copy and 220 | distribute such modifications or work under the terms of Section 1 221 | above, provided that you also meet all of these conditions: 222 | 223 | a) You must cause the modified files to carry prominent notices 224 | stating that you changed the files and the date of any change. 225 | 226 | b) You must cause any work that you distribute or publish, that in 227 | whole or in part contains or is derived from the Program or any 228 | part thereof, to be licensed as a whole at no charge to all third 229 | parties under the terms of this License. 230 | 231 | c) If the modified program normally reads commands interactively 232 | when run, you must cause it, when started running for such 233 | interactive use in the most ordinary way, to print or display an 234 | announcement including an appropriate copyright notice and a 235 | notice that there is no warranty (or else, saying that you provide 236 | a warranty) and that users may redistribute the program under 237 | these conditions, and telling the user how to view a copy of this 238 | License. (Exception: if the Program itself is interactive but 239 | does not normally print such an announcement, your work based on 240 | the Program is not required to print an announcement.) 241 | 242 | These requirements apply to the modified work as a whole. If 243 | identifiable sections of that work are not derived from the Program, 244 | and can be reasonably considered independent and separate works in 245 | themselves, then this License, and its terms, do not apply to those 246 | sections when you distribute them as separate works. But when you 247 | distribute the same sections as part of a whole which is a work based 248 | on the Program, the distribution of the whole must be on the terms of 249 | this License, whose permissions for other licensees extend to the 250 | entire whole, and thus to each and every part regardless of who wrote it. 251 | 252 | Thus, it is not the intent of this section to claim rights or contest 253 | your rights to work written entirely by you; rather, the intent is to 254 | exercise the right to control the distribution of derivative or 255 | collective works based on the Program. 256 | 257 | In addition, mere aggregation of another work not based on the Program 258 | with the Program (or with a work based on the Program) on a volume of 259 | a storage or distribution medium does not bring the other work under 260 | the scope of this License. 261 | 262 | 3. You may copy and distribute the Program (or a work based on it, 263 | under Section 2) in object code or executable form under the terms of 264 | Sections 1 and 2 above provided that you also do one of the following: 265 | 266 | a) Accompany it with the complete corresponding machine-readable 267 | source code, which must be distributed under the terms of Sections 268 | 1 and 2 above on a medium customarily used for software interchange; or, 269 | 270 | b) Accompany it with a written offer, valid for at least three 271 | years, to give any third party, for a charge no more than your 272 | cost of physically performing source distribution, a complete 273 | machine-readable copy of the corresponding source code, to be 274 | distributed under the terms of Sections 1 and 2 above on a medium 275 | customarily used for software interchange; or, 276 | 277 | c) Accompany it with the information you received as to the offer 278 | to distribute corresponding source code. (This alternative is 279 | allowed only for noncommercial distribution and only if you 280 | received the program in object code or executable form with such 281 | an offer, in accord with Subsection b above.) 282 | 283 | The source code for a work means the preferred form of the work for 284 | making modifications to it. For an executable work, complete source 285 | code means all the source code for all modules it contains, plus any 286 | associated interface definition files, plus the scripts used to 287 | control compilation and installation of the executable. However, as a 288 | special exception, the source code distributed need not include 289 | anything that is normally distributed (in either source or binary 290 | form) with the major components (compiler, kernel, and so on) of the 291 | operating system on which the executable runs, unless that component 292 | itself accompanies the executable. 293 | 294 | If distribution of executable or object code is made by offering 295 | access to copy from a designated place, then offering equivalent 296 | access to copy the source code from the same place counts as 297 | distribution of the source code, even though third parties are not 298 | compelled to copy the source along with the object code. 299 | 300 | 4. You may not copy, modify, sublicense, or distribute the Program 301 | except as expressly provided under this License. Any attempt 302 | otherwise to copy, modify, sublicense or distribute the Program is 303 | void, and will automatically terminate your rights under this License. 304 | However, parties who have received copies, or rights, from you under 305 | this License will not have their licenses terminated so long as such 306 | parties remain in full compliance. 307 | 308 | 5. You are not required to accept this License, since you have not 309 | signed it. However, nothing else grants you permission to modify or 310 | distribute the Program or its derivative works. These actions are 311 | prohibited by law if you do not accept this License. Therefore, by 312 | modifying or distributing the Program (or any work based on the 313 | Program), you indicate your acceptance of this License to do so, and 314 | all its terms and conditions for copying, distributing or modifying 315 | the Program or works based on it. 316 | 317 | 6. Each time you redistribute the Program (or any work based on the 318 | Program), the recipient automatically receives a license from the 319 | original licensor to copy, distribute or modify the Program subject to 320 | these terms and conditions. You may not impose any further 321 | restrictions on the recipients' exercise of the rights granted herein. 322 | You are not responsible for enforcing compliance by third parties to 323 | this License. 324 | 325 | 7. If, as a consequence of a court judgment or allegation of patent 326 | infringement or for any other reason (not limited to patent issues), 327 | conditions are imposed on you (whether by court order, agreement or 328 | otherwise) that contradict the conditions of this License, they do not 329 | excuse you from the conditions of this License. If you cannot 330 | distribute so as to satisfy simultaneously your obligations under this 331 | License and any other pertinent obligations, then as a consequence you 332 | may not distribute the Program at all. For example, if a patent 333 | license would not permit royalty-free redistribution of the Program by 334 | all those who receive copies directly or indirectly through you, then 335 | the only way you could satisfy both it and this License would be to 336 | refrain entirely from distribution of the Program. 337 | 338 | If any portion of this section is held invalid or unenforceable under 339 | any particular circumstance, the balance of the section is intended to 340 | apply and the section as a whole is intended to apply in other 341 | circumstances. 342 | 343 | It is not the purpose of this section to induce you to infringe any 344 | patents or other property right claims or to contest validity of any 345 | such claims; this section has the sole purpose of protecting the 346 | integrity of the free software distribution system, which is 347 | implemented by public license practices. Many people have made 348 | generous contributions to the wide range of software distributed 349 | through that system in reliance on consistent application of that 350 | system; it is up to the author/donor to decide if he or she is willing 351 | to distribute software through any other system and a licensee cannot 352 | impose that choice. 353 | 354 | This section is intended to make thoroughly clear what is believed to 355 | be a consequence of the rest of this License. 356 | 357 | 8. If the distribution and/or use of the Program is restricted in 358 | certain countries either by patents or by copyrighted interfaces, the 359 | original copyright holder who places the Program under this License 360 | may add an explicit geographical distribution limitation excluding 361 | those countries, so that distribution is permitted only in or among 362 | countries not thus excluded. In such case, this License incorporates 363 | the limitation as if written in the body of this License. 364 | 365 | 9. The Free Software Foundation may publish revised and/or new versions 366 | of the General Public License from time to time. Such new versions will 367 | be similar in spirit to the present version, but may differ in detail to 368 | address new problems or concerns. 369 | 370 | Each version is given a distinguishing version number. If the Program 371 | specifies a version number of this License which applies to it and "any 372 | later version", you have the option of following the terms and conditions 373 | either of that version or of any later version published by the Free 374 | Software Foundation. If the Program does not specify a version number of 375 | this License, you may choose any version ever published by the Free Software 376 | Foundation. 377 | 378 | 10. If you wish to incorporate parts of the Program into other free 379 | programs whose distribution conditions are different, write to the author 380 | to ask for permission. For software which is copyrighted by the Free 381 | Software Foundation, write to the Free Software Foundation; we sometimes 382 | make exceptions for this. Our decision will be guided by the two goals 383 | of preserving the free status of all derivatives of our free software and 384 | of promoting the sharing and reuse of software generally. 385 | 386 | NO WARRANTY 387 | 388 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 389 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 390 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 391 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 392 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 393 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 394 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 395 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 396 | REPAIR OR CORRECTION. 397 | 398 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 399 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 400 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 401 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 402 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 403 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 404 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 405 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 406 | POSSIBILITY OF SUCH DAMAGES. 407 | 408 | END OF TERMS AND CONDITIONS 409 | 410 | ================================ 411 | Apache License 2.0 412 | ================================ 413 | 414 | Apache License 415 | Version 2.0, January 2004 416 | http://www.apache.org/licenses/ 417 | 418 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 419 | 420 | 1. Definitions. 421 | 422 | "License" shall mean the terms and conditions for use, reproduction, 423 | and distribution as defined by Sections 1 through 9 of this document. 424 | 425 | "Licensor" shall mean the copyright owner or entity authorized by 426 | the copyright owner that is granting the License. 427 | 428 | "Legal Entity" shall mean the union of the acting entity and all 429 | other entities that control, are controlled by, or are under common 430 | control with that entity. For the purposes of this definition, 431 | "control" means (i) the power, direct or indirect, to cause the 432 | direction or management of such entity, whether by contract or 433 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 434 | outstanding shares, or (iii) beneficial ownership of such entity. 435 | 436 | "You" (or "Your") shall mean an individual or Legal Entity 437 | exercising permissions granted by this License. 438 | 439 | "Source" form shall mean the preferred form for making modifications, 440 | including but not limited to software source code, documentation 441 | source, and configuration files. 442 | 443 | "Object" form shall mean any form resulting from mechanical 444 | transformation or translation of a Source form, including but 445 | not limited to compiled object code, generated documentation, 446 | and conversions to other media types. 447 | 448 | "Work" shall mean the work of authorship, whether in Source or 449 | Object form, made available under the License, as indicated by a 450 | copyright notice that is included in or attached to the work 451 | (an example is provided in the Appendix below). 452 | 453 | "Derivative Works" shall mean any work, whether in Source or Object 454 | form, that is based on (or derived from) the Work and for which the 455 | editorial revisions, annotations, elaborations, or other modifications 456 | represent, as a whole, an original work of authorship. For the purposes 457 | of this License, Derivative Works shall not include works that remain 458 | separable from, or merely link (or bind by name) to the interfaces of, 459 | the Work and Derivative Works thereof. 460 | 461 | "Contribution" shall mean any work of authorship, including 462 | the original version of the Work and any modifications or additions 463 | to that Work or Derivative Works thereof, that is intentionally 464 | submitted to Licensor for inclusion in the Work by the copyright owner 465 | or by an individual or Legal Entity authorized to submit on behalf of 466 | the copyright owner. For the purposes of this definition, "submitted" 467 | means any form of electronic, verbal, or written communication sent 468 | to the Licensor or its representatives, including but not limited to 469 | communication on electronic mailing lists, source code control systems, 470 | and issue tracking systems that are managed by, or on behalf of, the 471 | Licensor for the purpose of discussing and improving the Work, but 472 | excluding communication that is conspicuously marked or otherwise 473 | designated in writing by the copyright owner as "Not a Contribution." 474 | 475 | "Contributor" shall mean Licensor and any individual or Legal Entity 476 | on behalf of whom a Contribution has been received by Licensor and 477 | subsequently incorporated within the Work. 478 | 479 | 2. Grant of Copyright License. Subject to the terms and conditions of 480 | this License, each Contributor hereby grants to You a perpetual, 481 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 482 | copyright license to reproduce, prepare Derivative Works of, 483 | publicly display, publicly perform, sublicense, and distribute the 484 | Work and such Derivative Works in Source or Object form. 485 | 486 | 3. Grant of Patent License. Subject to the terms and conditions of 487 | this License, each Contributor hereby grants to You a perpetual, 488 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 489 | (except as stated in this section) patent license to make, have made, 490 | use, offer to sell, sell, import, and otherwise transfer the Work, 491 | where such license applies only to those patent claims licensable 492 | by such Contributor that are necessarily infringed by their 493 | Contribution(s) alone or by combination of their Contribution(s) 494 | with the Work to which such Contribution(s) was submitted. If You 495 | institute patent litigation against any entity (including a 496 | cross-claim or counterclaim in a lawsuit) alleging that the Work 497 | or a Contribution incorporated within the Work constitutes direct 498 | or contributory patent infringement, then any patent licenses 499 | granted to You under this License for that Work shall terminate 500 | as of the date such litigation is filed. 501 | 502 | 4. Redistribution. You may reproduce and distribute copies of the 503 | Work or Derivative Works thereof in any medium, with or without 504 | modifications, and in Source or Object form, provided that You 505 | meet the following conditions: 506 | 507 | (a) You must give any other recipients of the Work or 508 | Derivative Works a copy of this License; and 509 | 510 | (b) You must cause any modified files to carry prominent notices 511 | stating that You changed the files; and 512 | 513 | (c) You must retain, in the Source form of any Derivative Works 514 | that You distribute, all copyright, patent, trademark, and 515 | attribution notices from the Source form of the Work, 516 | excluding those notices that do not pertain to any part of 517 | the Derivative Works; and 518 | 519 | (d) If the Work includes a "NOTICE" text file as part of its 520 | distribution, then any Derivative Works that You distribute must 521 | include a readable copy of the attribution notices contained 522 | within such NOTICE file, excluding those notices that do not 523 | pertain to any part of the Derivative Works, in at least one 524 | of the following places: within a NOTICE text file distributed 525 | as part of the Derivative Works; within the Source form or 526 | documentation, if provided along with the Derivative Works; or, 527 | within a display generated by the Derivative Works, if and 528 | wherever such third-party notices normally appear. The contents 529 | of the NOTICE file are for informational purposes only and 530 | do not modify the License. You may add Your own attribution 531 | notices within Derivative Works that You distribute, alongside 532 | or as an addendum to the NOTICE text from the Work, provided 533 | that such additional attribution notices cannot be construed 534 | as modifying the License. 535 | 536 | You may add Your own copyright statement to Your modifications and 537 | may provide additional or different license terms and conditions 538 | for use, reproduction, or distribution of Your modifications, or 539 | for any such Derivative Works as a whole, provided Your use, 540 | reproduction, and distribution of the Work otherwise complies with 541 | the conditions stated in this License. 542 | 543 | 5. Submission of Contributions. Unless You explicitly state otherwise, 544 | any Contribution intentionally submitted for inclusion in the Work 545 | by You to the Licensor shall be under the terms and conditions of 546 | this License, without any additional terms or conditions. 547 | Notwithstanding the above, nothing herein shall supersede or modify 548 | the terms of any separate license agreement you may have executed 549 | with Licensor regarding such Contributions. 550 | 551 | 6. Trademarks. This License does not grant permission to use the trade 552 | names, trademarks, service marks, or product names of the Licensor, 553 | except as required for reasonable and customary use in describing the 554 | origin of the Work and reproducing the content of the NOTICE file. 555 | 556 | 7. Disclaimer of Warranty. Unless required by applicable law or 557 | agreed to in writing, Licensor provides the Work (and each 558 | Contributor provides its Contributions) on an "AS IS" BASIS, 559 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 560 | implied, including, without limitation, any warranties or conditions 561 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 562 | PARTICULAR PURPOSE. You are solely responsible for determining the 563 | appropriateness of using or redistributing the Work and assume any 564 | risks associated with Your exercise of permissions under this License. 565 | 566 | 8. Limitation of Liability. In no event and under no legal theory, 567 | whether in tort (including negligence), contract, or otherwise, 568 | unless required by applicable law (such as deliberate and grossly 569 | negligent acts) or agreed to in writing, shall any Contributor be 570 | liable to You for damages, including any direct, indirect, special, 571 | incidental, or consequential damages of any character arising as a 572 | result of this License or out of the use or inability to use the 573 | Work (including but not limited to damages for loss of goodwill, 574 | work stoppage, computer failure or malfunction, or any and all 575 | other commercial damages or losses), even if such Contributor 576 | has been advised of the possibility of such damages. 577 | 578 | 9. Accepting Warranty or Additional Liability. While redistributing 579 | the Work or Derivative Works thereof, You may choose to offer, 580 | and charge a fee for, acceptance of support, warranty, indemnity, 581 | or other liability obligations and/or rights consistent with this 582 | License. However, in accepting such obligations, You may act only 583 | on Your own behalf and on Your sole responsibility, not on behalf 584 | of any other Contributor, and only if You agree to indemnify, 585 | defend, and hold each Contributor harmless for any liability 586 | incurred by, or claims asserted against, such Contributor by reason 587 | of your accepting any such warranty or additional liability. 588 | 589 | END OF TERMS AND CONDITIONS 590 | 591 | APPENDIX: How to apply the Apache License to your work. 592 | 593 | To apply the Apache License to your work, attach the following 594 | boilerplate notice, with the fields enclosed by brackets "[]" 595 | replaced with your own identifying information. (Don't include 596 | the brackets!) The text should be enclosed in the appropriate 597 | comment syntax for the file format. We also recommend that a 598 | file or class name and description of purpose be included on the 599 | same "printed page" as the copyright notice for easier 600 | identification within third-party archives. 601 | 602 | Copyright [yyyy] [name of copyright owner] 603 | 604 | Licensed under the Apache License, Version 2.0 (the "License"); 605 | you may not use this file except in compliance with the License. 606 | You may obtain a copy of the License at 607 | 608 | http://www.apache.org/licenses/LICENSE-2.0 609 | 610 | Unless required by applicable law or agreed to in writing, software 611 | distributed under the License is distributed on an "AS IS" BASIS, 612 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 613 | See the License for the specific language governing permissions and 614 | limitations under the License. 615 | 616 | =========== 617 | MIT License 618 | =========== 619 | 620 | Permission is hereby granted, free of charge, to any person obtaining a copy 621 | of this software and associated documentation files (the "Software"), to deal 622 | in the Software without restriction, including without limitation the rights 623 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 624 | copies of the Software, and to permit persons to whom the Software is 625 | furnished to do so, subject to the following conditions: 626 | 627 | The above copyright notice and this permission notice shall be included in all 628 | copies or substantial portions of the Software. 629 | 630 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 631 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 632 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 633 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 634 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 635 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 636 | SOFTWARE. 637 | -------------------------------------------------------------------------------- /bin/LAN.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JugAndDoubleTap/LanLauncher/14424acd8c7e71572661e2a552226648ba346e49/bin/LAN.ico -------------------------------------------------------------------------------- /bin/LLUpdater.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JugAndDoubleTap/LanLauncher/14424acd8c7e71572661e2a552226648ba346e49/bin/LLUpdater.exe -------------------------------------------------------------------------------- /bin/LanLauncher.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JugAndDoubleTap/LanLauncher/14424acd8c7e71572661e2a552226648ba346e49/bin/LanLauncher.exe -------------------------------------------------------------------------------- /update.ini: -------------------------------------------------------------------------------- 1 | [Update] 2 | 3 | update number = 2.0.0 4 | --------------------------------------------------------------------------------