diff --git a/changelog.md b/changelog.md index 002dc15a..b98a9459 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,18 @@ +#2.0 RC 7 + +##Features/Improvements : +* **[Units/Factions]** Added P-47D-30 for factions allies_1944 +* **[Mission Generator]** CAP flights have been slightly reworked +* **[Mission Generator]** Add PP points for JF-17 on STRIKE missions +* **[Mission Generator]** Add ST point for F-14B on STRIKE missions +* **[Mission Generator]** Flights with client slots will never be delayed +* **[Mission Generator]** AI units can start from parking + +##Fixed issues : +* **[Mission Generator]** When playing as RED the activation trigger would not be properly generated +* **[Mission Generator]** Changed "strike" payload for Su-24M +* **[Mission Generator]** FW-190A8 is now properly considered as flyable + #2.0 RC 6 Saves file from RC5 are not compatible with the new version. diff --git a/game/settings.py b/game/settings.py index d399803a..547c6ab0 100644 --- a/game/settings.py +++ b/game/settings.py @@ -21,5 +21,6 @@ class Settings: perf_artillery = True perf_moving_units = True perf_infantry = True + perf_ai_parking_start = True diff --git a/gen/flights/ai_flight_planner_db.py b/gen/flights/ai_flight_planner_db.py index e8eed392..d43167e8 100644 --- a/gen/flights/ai_flight_planner_db.py +++ b/gen/flights/ai_flight_planner_db.py @@ -48,6 +48,7 @@ CAP_CAPABLE = [ P_51D_30_NA, P_51D, + P_47D_30, SpitfireLFMkIXCW, SpitfireLFMkIX, @@ -106,6 +107,7 @@ CAS_CAPABLE = [ P_51D_30_NA, P_51D, + P_47D_30, A_20G, SpitfireLFMkIXCW, @@ -169,6 +171,7 @@ STRIKE_CAPABLE = [ P_51D_30_NA, P_51D, + P_47D_30, A_20G, SpitfireLFMkIXCW, diff --git a/qt_ui/main.py b/qt_ui/main.py index b71975a8..649a3ec5 100644 --- a/qt_ui/main.py +++ b/qt_ui/main.py @@ -4,6 +4,7 @@ import sys from shutil import copyfile import dcs +from PySide2 import QtWidgets from PySide2.QtGui import QPixmap from PySide2.QtWidgets import QApplication, QSplashScreen from dcs import installation @@ -11,12 +12,22 @@ from dcs import installation from qt_ui import uiconstants from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.QLiberationWindow import QLiberationWindow -from userdata import persistency, logging as logging_module +from qt_ui.windows.preferences.QLiberationFirstStartWindow import QLiberationFirstStartWindow +from userdata import persistency, logging as logging_module, liberation_install if __name__ == "__main__": - persistency.setup(installation.get_dcs_saved_games_directory()) + app = QApplication(sys.argv) + css = "" + with open("./resources/stylesheets/style.css") as stylesheet: + app.setStyleSheet(stylesheet.read()) + + # Logging setup + VERSION_STRING = "2.0RC6" + logging_module.setup_version_string(VERSION_STRING) + + # Inject custom payload in pydcs framework custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..\\resources\\customized_payloads") if os.path.exists(custom_payloads): dcs.planes.FlyingType.payload_dirs.append(custom_payloads) @@ -27,11 +38,14 @@ if __name__ == "__main__": if os.path.exists(custom_payloads): dcs.planes.FlyingType.payload_dirs.append(custom_payloads) - VERSION_STRING = "2.0RC6" - logging_module.setup_version_string(VERSION_STRING) - logging.info("Using {} as userdata folder".format(persistency.base_path())) - app = QApplication(sys.argv) + first_start = liberation_install.init() + if first_start: + window = QLiberationFirstStartWindow() + window.exec_() + + logging.info("Using {} as 'Saved Game Folder'".format(persistency.base_path())) + logging.info("Using {} as 'DCS installation folder'".format(liberation_install.get_dcs_install_directory())) # Splash screen setup pixmap = QPixmap("./resources/ui/splash_screen.png") @@ -44,24 +58,28 @@ if __name__ == "__main__": uiconstants.load_aircraft_icons() uiconstants.load_vehicle_icons() - - css = "" - with open("./resources/stylesheets/style.css") as stylesheet: - css = stylesheet.read() - # Replace DCS Mission scripting file to allow DCS Liberation to work - print("Replace : " + installation.get_dcs_install_directory() + os.path.sep + "Scripts/MissionScripting.lua") - copyfile("./resources/scripts/MissionScripting.lua", installation.get_dcs_install_directory() + os.path.sep + "Scripts/MissionScripting.lua") - app.processEvents() + try: + liberation_install.replace_mission_scripting_file() + except: + error_dialog = QtWidgets.QErrorMessage() + error_dialog.setWindowTitle("Wrong DCS installation directory.") + error_dialog.showMessage("Unable to modify Mission Scripting file. Possible issues with rights. Try running as admin, or please perform the modification of the MissionScripting file manually.") + error_dialog.exec_() # Apply CSS (need works) - app.setStyleSheet(css) GameUpdateSignal() # Start window window = QLiberationWindow() window.showMaximized() - splash.finish(window) - sys.exit(app.exec_()) + qt_execution_code = app.exec_() + + # Restore Mission Scripting file + logging.info("QT App terminated with status code : " + str(qt_execution_code)) + logging.info("Attempt to restore original mission scripting file") + liberation_install.restore_original_mission_scripting() + sys.exit(qt_execution_code) + diff --git a/qt_ui/uiconstants.py b/qt_ui/uiconstants.py index 63e8ccb2..0fe11d9e 100644 --- a/qt_ui/uiconstants.py +++ b/qt_ui/uiconstants.py @@ -8,12 +8,12 @@ from game.event import UnitsDeliveryEvent, FrontlineAttackEvent from theater.theatergroundobject import CATEGORY_MAP URLS : Dict[str, str] = { - "Manual": "https://github.com/shdwp/dcs_liberation/wiki/Manual", + "Manual": "https://github.com/khopa/dcs_liberation/wiki", "Troubleshooting": "https://github.com/shdwp/dcs_liberation/wiki/Troubleshooting", "Modding": "https://github.com/shdwp/dcs_liberation/wiki/Modding-tutorial", - "Repository": "https://github.com/shdwp/dcs_liberation", + "Repository": "https://github.com/khopa/dcs_liberation", "ForumThread": "https://forums.eagle.ru/showthread.php?t=214834", - "Issues": "https://github.com/shdwp/dcs_liberation/issues" + "Issues": "https://github.com/khopa/dcs_liberation/issues" } LABELS_OPTIONS = ["Full", "Abbreviated", "Dot Only", "Off"] diff --git a/qt_ui/windows/QLiberationWindow.py b/qt_ui/windows/QLiberationWindow.py index 31ba3bac..605ad1e5 100644 --- a/qt_ui/windows/QLiberationWindow.py +++ b/qt_ui/windows/QLiberationWindow.py @@ -1,10 +1,9 @@ import sys import webbrowser -from PySide2 import QtGui from PySide2.QtCore import Qt from PySide2.QtGui import QIcon -from PySide2.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QMainWindow, QAction, QMessageBox, QDesktopWidget, \ +from PySide2.QtWidgets import QWidget, QVBoxLayout, QMainWindow, QAction, QMessageBox, QDesktopWidget, \ QSplitter import qt_ui.uiconstants as CONST @@ -12,10 +11,12 @@ from game import Game from qt_ui.uiconstants import URLS from qt_ui.widgets.QTopPanel import QTopPanel from qt_ui.widgets.map.QLiberationMap import QLiberationMap +from qt_ui.windows.preferences import QLiberationPreferences from qt_ui.windows.GameUpdateSignal import GameUpdateSignal, DebriefingSignal from qt_ui.windows.QDebriefingWindow import QDebriefingWindow from qt_ui.windows.QNewGameWizard import NewGameWizard from qt_ui.windows.infos.QInfoPanel import QInfoPanel +from qt_ui.windows.preferences.QLiberationPreferencesWindow import QLiberationPreferencesWindow from userdata import persistency @@ -79,6 +80,10 @@ class QLiberationWindow(QMainWindow): self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about")) self.showAboutDialogAction.triggered.connect(self.showAboutDialog) + self.showLiberationPrefDialogAction = QAction("Preferences", self) + self.showLiberationPrefDialogAction.setIcon(QIcon.fromTheme("help-about")) + self.showLiberationPrefDialogAction.triggered.connect(self.showLiberationDialog) + def initToolbar(self): self.tool_bar = self.addToolBar("File") self.tool_bar.addAction(self.newGameAction) @@ -92,17 +97,21 @@ class QLiberationWindow(QMainWindow): file_menu.addAction(self.newGameAction) #file_menu.addAction(QIcon(CONST.ICONS["Open"]), "Open") # TODO : implement file_menu.addAction(self.saveGameAction) + file_menu.addSeparator() + file_menu.addAction(self.showLiberationPrefDialogAction) + file_menu.addSeparator() #file_menu.addAction("Save As") # TODO : implement #file_menu.addAction("Close Current Game", lambda: self.closeGame()) # Not working file_menu.addAction("Exit" , lambda: self.exit()) help_menu = self.menu.addMenu("Help") - #help_menu.addAction("Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"])) + help_menu.addAction("Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"])) + help_menu.addAction("Discord", lambda: webbrowser.open_new_tab("https://" + "discord.gg" + "/" + "bKrt" + "rkJ")) #help_menu.addAction("Troubleshooting Guide", lambda: webbrowser.open_new_tab(URLS["Troubleshooting"])) #help_menu.addAction("Modding Guide", lambda: webbrowser.open_new_tab(URLS["Modding"])) #help_menu.addSeparator() ----> Note from Khopa : I disable these links since it's not up to date for this branch - help_menu.addAction("Contribute", lambda: webbrowser.open_new_tab(URLS["Repository"])) + #help_menu.addAction("Contribute", lambda: webbrowser.open_new_tab(URLS["Repository"])) help_menu.addAction("Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"])) help_menu.addAction("Report an issue", lambda: webbrowser.open_new_tab(URLS["Issues"])) help_menu.addSeparator() @@ -192,6 +201,10 @@ class QLiberationWindow(QMainWindow): print(about.textFormat()) about.exec_() + def showLiberationDialog(self): + self.subwindow = QLiberationPreferencesWindow() + self.subwindow.show() + def onDebriefing(self, debrief: DebriefingSignal): print("On Debriefing") self.debriefing = QDebriefingWindow(debrief.debriefing, debrief.gameEvent, debrief.game) diff --git a/qt_ui/windows/preferences/QLiberationFirstStartWindow.py b/qt_ui/windows/preferences/QLiberationFirstStartWindow.py new file mode 100644 index 00000000..f441f2d3 --- /dev/null +++ b/qt_ui/windows/preferences/QLiberationFirstStartWindow.py @@ -0,0 +1,80 @@ +from PySide2.QtGui import QIcon, Qt +from PySide2.QtWidgets import QDialog, QVBoxLayout, QPushButton, QHBoxLayout, QPlainTextEdit, QTextEdit + +from qt_ui.windows.preferences.QLiberationPreferences import QLiberationPreferences + + +class QLiberationFirstStartWindow(QDialog): + + def __init__(self): + super(QLiberationFirstStartWindow, self).__init__() + + self.setModal(True) + self.setWindowTitle("First start configuration") + self.setMinimumSize(500, 200) + self.setWindowIcon(QIcon("./resources/icon.png")) + self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.Dialog | Qt.WindowTitleHint) + self.setWindowModality(Qt.WindowModal) + self.preferences = QLiberationPreferences() + + WARN_TEXT = """ + Welcome to DCS Liberation ! +

+ Please take 30 seconds to read this : + +

DCS Liberation will modify this file in your DCS installation directory :

+
+ <dcs_installation_directory>/Scripts/MissionScripting.lua
+ +

+ This will disable some security limits of the DCS World Lua scripting environment, in order to allow communication between DCS World and DCS Liberation. + However, the modification of this file could potentially grant access to your filesystem to malicious DCS mission files. +

+ +

So, you should not join untrusted servers or open untrusted mission files within DCS world while DCS Liberation is running.

+ +

+ DCS Liberation will restore your original MissionScripting file when it close. +

+ +

+ However, should DCS Liberation encounter an unexpected crash (which should not happen), the MissionScripting file might not be restored. + If that occurs, you can use the backup file saved in the DCS Liberation directory there : +

+ +
+ ./resources/scripts/MissionScripting.original.lua
+ +

Then copy it in your DCS installation directory to replace this file :

+ +
+ <dcs_installation_directory>/Scripts/MissionScripting.lua
+ +

As you click on the button below, the file will be replaced in your DCS installation directory.

+ +

+ + Thank you for reading ! + """ + self.warning_text = QTextEdit(WARN_TEXT) + self.warning_text.setReadOnly(True) + self.apply_button = QPushButton("I have read everything and I Accept") + self.apply_button.clicked.connect(lambda : self.apply()) + self.initUI() + + def initUI(self): + layout = QVBoxLayout() + layout.addWidget(self.preferences) + layout.addWidget(self.warning_text) + layout.addStretch() + apply_btn_layout = QHBoxLayout() + apply_btn_layout.addStretch() + apply_btn_layout.addWidget(self.apply_button) + layout.addLayout(apply_btn_layout) + self.setLayout(layout) + + def apply(self): + print("Applying changes") + if self.preferences.apply(): + self.close() + diff --git a/qt_ui/windows/preferences/QLiberationPreferences.py b/qt_ui/windows/preferences/QLiberationPreferences.py new file mode 100644 index 00000000..eba000fe --- /dev/null +++ b/qt_ui/windows/preferences/QLiberationPreferences.py @@ -0,0 +1,93 @@ +import os + +from PySide2 import QtWidgets +from PySide2.QtGui import Qt +from PySide2.QtWidgets import QFrame, QLineEdit, QGridLayout, QVBoxLayout, QLabel, QPushButton, \ + QFileDialog, QMessageBox, QDialog + +from userdata import liberation_install + + +class QLiberationPreferences(QFrame): + + def __init__(self): + super(QLiberationPreferences, self).__init__() + self.saved_game_dir = "" + self.dcs_install_dir = "" + + self.dcs_install_dir = liberation_install.get_dcs_install_directory() + self.saved_game_dir = liberation_install.get_saved_game_dir() + + self.edit_dcs_install_dir = QLineEdit(self.dcs_install_dir) + self.edit_saved_game_dir = QLineEdit(self.saved_game_dir) + + self.edit_dcs_install_dir.setMinimumWidth(300) + self.edit_saved_game_dir.setMinimumWidth(300) + + self.browse_saved_game = QPushButton("Browse...") + self.browse_saved_game.clicked.connect(self.on_browse_saved_games) + self.browse_install_dir = QPushButton("Browse...") + self.browse_install_dir.clicked.connect(self.on_browse_installation_dir) + + self.initUi() + + def initUi(self): + main_layout = QVBoxLayout() + layout = QGridLayout() + layout.addWidget(QLabel("DCS saved game directory:"), 0, 0, alignment=Qt.AlignLeft) + layout.addWidget(self.edit_saved_game_dir, 1, 0, alignment=Qt.AlignRight) + layout.addWidget(self.browse_saved_game, 1, 1, alignment=Qt.AlignRight) + layout.addWidget(QLabel("DCS installation directory:"), 2, 0, alignment=Qt.AlignLeft) + layout.addWidget(self.edit_dcs_install_dir, 3, 0, alignment=Qt.AlignRight) + layout.addWidget(self.browse_install_dir, 3, 1, alignment=Qt.AlignRight) + + main_layout.addLayout(layout) + main_layout.addStretch() + + self.setLayout(main_layout) + + def on_browse_saved_games(self): + saved_game_dir = str(QFileDialog.getExistingDirectory(self, "Select DCS Saved Game Directory")) + if saved_game_dir: + self.saved_game_dir = saved_game_dir + self.edit_saved_game_dir.setText(saved_game_dir) + + def on_browse_installation_dir(self): + install_dir = str(QFileDialog.getExistingDirectory(self, "Select DCS Installation Directory")) + if install_dir: + self.dcs_install_dir = install_dir + self.edit_dcs_install_dir.setText(install_dir) + + def apply(self): + + print("Applying changes") + self.saved_game_dir = self.edit_saved_game_dir.text() + self.dcs_install_dir = self.edit_dcs_install_dir.text() + + if not os.path.isdir(self.saved_game_dir): + error_dialog = QMessageBox.critical(self, "Wrong DCS Saved Games directory.", + self.saved_game_dir + " is not a valid directory", + QMessageBox.StandardButton.Ok) + error_dialog.exec_() + return False + + if not os.path.isdir(self.dcs_install_dir): + error_dialog = QMessageBox.critical(self, "Wrong DCS installation directory.", + self.dcs_install_dir + " is not a valid directory", + QMessageBox.StandardButton.Ok) + error_dialog.exec_() + return False + + if not os.path.isdir(os.path.join(self.dcs_install_dir, "Scripts")) and os.path.isfile(os.path.join(self.dcs_install_dir, "bin", "DCS.exe")): + error_dialog = QMessageBox.critical(self, "Wrong DCS installation directory.", + self.dcs_install_dir + " is not a valid DCS installation directory", + QMessageBox.StandardButton.Ok) + error_dialog.exec_() + return False + + liberation_install.setup(self.saved_game_dir, self.dcs_install_dir) + liberation_install.save_config() + return True + + + diff --git a/qt_ui/windows/preferences/QLiberationPreferencesWindow.py b/qt_ui/windows/preferences/QLiberationPreferencesWindow.py new file mode 100644 index 00000000..ce5a65ff --- /dev/null +++ b/qt_ui/windows/preferences/QLiberationPreferencesWindow.py @@ -0,0 +1,37 @@ +from PySide2.QtGui import QIcon, Qt +from PySide2.QtWidgets import QDialog, QVBoxLayout, QPushButton, QHBoxLayout + +from qt_ui.windows.preferences.QLiberationPreferences import QLiberationPreferences + + +class QLiberationPreferencesWindow(QDialog): + + def __init__(self): + super(QLiberationPreferencesWindow, self).__init__() + + self.setModal(True) + self.setWindowTitle("Preferences") + self.setMinimumSize(300, 200) + self.setWindowIcon(QIcon("./resources/icon.png")) + self.preferences = QLiberationPreferences() + self.apply_button = QPushButton("Apply") + self.apply_button.clicked.connect(lambda : self.apply()) + self.initUI() + + def initUI(self): + layout = QVBoxLayout() + layout.addWidget(self.preferences) + layout.addStretch() + apply_btn_layout = QHBoxLayout() + apply_btn_layout.addStretch() + apply_btn_layout.addWidget(self.apply_button) + layout.addLayout(apply_btn_layout) + self.setLayout(layout) + + def apply(self): + if self.preferences.apply(): + print("Closing") + self.close() + else: + print("Not Closing") + diff --git a/qt_ui/windows/settings/QSettingsWindow.py b/qt_ui/windows/settings/QSettingsWindow.py index efec774f..3f660ed3 100644 --- a/qt_ui/windows/settings/QSettingsWindow.py +++ b/qt_ui/windows/settings/QSettingsWindow.py @@ -98,11 +98,11 @@ class QSettingsWindow(QDialog): self.enemyAASkill.currentIndexChanged.connect(self.applySettings) self.difficultyLayout.addWidget(QLabel("Player coalition skill"), 0, 0) - self.difficultyLayout.addWidget(self.playerCoalitionSkill, 0, 1) + self.difficultyLayout.addWidget(self.playerCoalitionSkill, 0, 1, Qt.AlignRight) self.difficultyLayout.addWidget(QLabel("Enemy skill"), 1, 0) - self.difficultyLayout.addWidget(self.enemyCoalitionSkill, 1, 1) + self.difficultyLayout.addWidget(self.enemyCoalitionSkill, 1, 1, Qt.AlignRight) self.difficultyLayout.addWidget(QLabel("Enemy AA and vehicles skill"), 2, 0) - self.difficultyLayout.addWidget(self.enemyAASkill, 2, 1) + self.difficultyLayout.addWidget(self.enemyAASkill, 2, 1, Qt.AlignRight) self.difficultyLabel = QComboBox() [self.difficultyLabel.addItem(t) for t in CONST.LABELS_OPTIONS] @@ -110,13 +110,13 @@ class QSettingsWindow(QDialog): self.difficultyLabel.currentIndexChanged.connect(self.applySettings) self.difficultyLayout.addWidget(QLabel("In Game Labels"), 3, 0) - self.difficultyLayout.addWidget(self.difficultyLabel, 3, 1) + self.difficultyLayout.addWidget(self.difficultyLabel, 3, 1, Qt.AlignRight) self.noNightMission = QCheckBox() self.noNightMission.setChecked(self.game.settings.night_disabled) self.noNightMission.toggled.connect(self.applySettings) self.difficultyLayout.addWidget(QLabel("No night missions"), 4, 0) - self.difficultyLayout.addWidget(self.noNightMission, 4, 1) + self.difficultyLayout.addWidget(self.noNightMission, 4, 1, Qt.AlignRight) def initGeneratorLayout(self): @@ -135,7 +135,7 @@ class QSettingsWindow(QDialog): self.supercarrier.toggled.connect(self.applySettings) self.gameplayLayout.addWidget(QLabel("Use Supercarrier Module"), 0, 0) - self.gameplayLayout.addWidget(self.supercarrier, 0, 1) + self.gameplayLayout.addWidget(self.supercarrier, 0, 1, Qt.AlignRight) self.performance = QGroupBox("Performance") self.performanceLayout = QGridLayout(); @@ -162,16 +162,22 @@ class QSettingsWindow(QDialog): self.infantry.setChecked(self.game.settings.perf_infantry) self.infantry.toggled.connect(self.applySettings) + self.ai_parking_start = QCheckBox() + self.ai_parking_start.setChecked(self.game.settings.perf_ai_parking_start) + self.ai_parking_start.toggled.connect(self.applySettings) + self.performanceLayout.addWidget(QLabel("Smoke visual effect on frontline"), 0, 0) - self.performanceLayout.addWidget(self.smoke, 0, 1) + self.performanceLayout.addWidget(self.smoke, 0, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("SAM starts in RED alert mode"), 1, 0) - self.performanceLayout.addWidget(self.red_alert, 1, 1) + self.performanceLayout.addWidget(self.red_alert, 1, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Artillery strikes"), 2, 0) - self.performanceLayout.addWidget(self.arti, 2, 1) + self.performanceLayout.addWidget(self.arti, 2, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Moving ground units"), 3, 0) - self.performanceLayout.addWidget(self.moving_units, 3, 1) + self.performanceLayout.addWidget(self.moving_units, 3, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Generate infantry squads along vehicles"), 4, 0) - self.performanceLayout.addWidget(self.infantry, 4, 1) + self.performanceLayout.addWidget(self.infantry, 4, 1, alignment=Qt.AlignRight) + self.performanceLayout.addWidget(QLabel("AI planes parking start (AI starts in flight if disabled)"), 5, 0) + self.performanceLayout.addWidget(self.ai_parking_start, 5, 1, alignment=Qt.AlignRight) self.generatorLayout.addWidget(self.gameplay) self.generatorLayout.addWidget(QLabel("Disabling settings below may improve performance, but will impact the overall quality of the experience.")) @@ -230,6 +236,7 @@ class QSettingsWindow(QDialog): self.game.settings.perf_artillery = self.arti.isChecked() self.game.settings.perf_moving_units = self.moving_units.isChecked() self.game.settings.perf_infantry = self.infantry.isChecked() + self.game.settings.perf_ai_parking_start = self.ai_parking_start.isChecked() GameUpdateSignal.get_instance().updateGame(self.game) diff --git a/userdata/dcs_environment.py b/userdata/dcs_environment.py deleted file mode 100644 index 0c5d1ce4..00000000 --- a/userdata/dcs_environment.py +++ /dev/null @@ -1,132 +0,0 @@ -""" -This utility classes provides methods to check players installed DCS environment. - -TODO : add method 'is_using_open_beta', 'is_using_stable' -TODO : [NICE to have] add method to check list of installed DCS modules (could be done either through window registry, or through filesystem analysis) -""" - -import winreg -import os - - -def is_using_dcs_steam_edition(): - """ - Check if DCS World : Steam Edition version is installed on this computer - :return True if DCS Steam edition is installed, - -1 if DCS Steam Edition is registered in Steam apps but not installed, - False if never installed in Steam - """ - try: - # Note : Steam App ID for DCS World is 223750 - dcs_steam_app_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Valve\\Steam\\Apps\\223750") - installed = winreg.QueryValueEx(dcs_steam_app_key, "Installed") - winreg.CloseKey(dcs_steam_app_key) - if installed[0] == 1: - return True - else: - return False - except FileNotFoundError as fnfe: - return False - - -def is_using_dcs_standalone_edition(): - """ - Check if DCS World standalone edition is installed on this computer - :return True if Standalone is installed, False if it is not - """ - try: - dcs_path_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Eagle Dynamics\\DCS World") - winreg.CloseKey(dcs_path_key) - return True - except FileNotFoundError as fnfe: - return False - - -def _find_steam_directory(): - """ - Get the Steam install directory for this computer from registry - :return Steam installation path - """ - try: - steam_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Valve\\Steam") - path = winreg.QueryValueEx(steam_key, "SteamPath")[0] - winreg.CloseKey(steam_key) - return path - except FileNotFoundError as fnfe: - print(fnfe) - return "" - - -def _get_steam_library_folders(): - """ - Get the installation directory for Steam games - :return List of Steam library folders where games can be installed - """ - try: - steam_dir = _find_steam_directory() - """ - For reference here is what the vdf file is supposed to look like : - - "LibraryFolders" - { - "TimeNextStatsReport" "1561832478" - "ContentStatsID" "-158337411110787451" - "1" "D:\\Games\\Steam" - "2" "E:\\Steam" - } - """ - vdf_file_location = steam_dir + os.path.sep + "steamapps" + os.path.sep + "libraryfolders.vdf" - with open(vdf_file_location) as adf_file: - paths = [l.split("\"")[3] for l in adf_file.readlines()[1:] if ':\\\\' in l] - return paths - except Exception as e: - print(e) - return [] - - -def _find_steam_dcs_directory(): - """ - Find the DCS install directory for DCS World Steam Edition - :return: Install directory as string, empty string if not found - """ - for library_folder in _get_steam_library_folders(): - folder = library_folder + os.path.sep + "steamapps" + os.path.sep + "common" + os.path.sep + "DCSWorld" - if os.path.isdir(folder): - return folder + os.path.sep - return "" - - -def get_dcs_install_directory(): - """ - Get the DCS World install directory for this computer - :return DCS World install directory - """ - if is_using_dcs_standalone_edition(): - try: - dcs_path_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Eagle Dynamics\\DCS World") - path = winreg.QueryValueEx(dcs_path_key, "Path") - dcs_dir = path[0] + os.path.sep - winreg.CloseKey(dcs_path_key) - return dcs_dir - except Exception as e: - print("Couldn't detect DCS World installation folder") - return "" - elif is_using_dcs_steam_edition(): - return _find_steam_dcs_directory() - else: - print("Couldn't detect any installed DCS World version") - - -def get_dcs_saved_games_directory(): - """ - Get the save game directory for DCS World - :return: Save game directory as string - """ - return os.path.join(os.path.expanduser("~"), "Saved Games", "DCS") - - -if __name__ == "__main__": - print("Using STEAM Edition : " + str(is_using_dcs_steam_edition())) - print("Using Standalone Edition : " + str(is_using_dcs_standalone_edition())) - print("DCS Installation directory : " + get_dcs_install_directory()) - print("DCS saved games directory : " + get_dcs_saved_games_directory()) \ No newline at end of file diff --git a/userdata/liberation_install.py b/userdata/liberation_install.py new file mode 100644 index 00000000..5f19ec0a --- /dev/null +++ b/userdata/liberation_install.py @@ -0,0 +1,99 @@ +import json +import os +from shutil import copyfile + +import dcs + +from userdata import persistency + +global __dcs_saved_game_directory +global __dcs_installation_directory + +PREFERENCES_FILE_PATH = "liberation_preferences.json" + +def init(): + global __dcs_saved_game_directory + global __dcs_installation_directory + + if os.path.isfile(PREFERENCES_FILE_PATH): + try: + with(open(PREFERENCES_FILE_PATH)) as prefs: + pref_data = json.loads(prefs.read()) + __dcs_saved_game_directory = pref_data["saved_game_dir"] + __dcs_installation_directory = pref_data["dcs_install_dir"] + is_first_start = False + except: + __dcs_saved_game_directory = "" + __dcs_installation_directory = "" + is_first_start = True + else: + try: + __dcs_saved_game_directory = dcs.installation.get_dcs_saved_games_directory() + if os.path.exists(__dcs_saved_game_directory + ".openbeta"): + __dcs_saved_game_directory = dcs.installation.get_dcs_saved_games_directory() + ".openbeta" + except: + __dcs_saved_game_directory = "" + try: + __dcs_installation_directory = dcs.installation.get_dcs_install_directory() + except: + __dcs_installation_directory = "" + + is_first_start = True + persistency.setup(__dcs_saved_game_directory) + return is_first_start + + +def setup(saved_game_dir, install_dir): + global __dcs_saved_game_directory + global __dcs_installation_directory + __dcs_saved_game_directory = saved_game_dir + __dcs_installation_directory = install_dir + persistency.setup(__dcs_saved_game_directory) + + +def save_config(): + global __dcs_saved_game_directory + global __dcs_installation_directory + pref_data = {"saved_game_dir": __dcs_saved_game_directory, + "dcs_install_dir": __dcs_installation_directory} + with(open(PREFERENCES_FILE_PATH, "w")) as prefs: + prefs.write(json.dumps(pref_data)) + + +def get_dcs_install_directory(): + global __dcs_installation_directory + return __dcs_installation_directory + + +def get_saved_game_dir(): + global __dcs_saved_game_directory + return __dcs_saved_game_directory + + +def replace_mission_scripting_file(): + install_dir = get_dcs_install_directory() + mission_scripting_path = os.path.join(install_dir, "Scripts", "MissionScripting.lua") + liberation_scripting_path = "./resources/scripts/MissionScripting.lua" + backup_scripting_path = "./resources/scripts/MissionScripting.original.lua" + if os.path.isfile(mission_scripting_path): + with open(mission_scripting_path, "r") as ms: + current_file_content = ms.read() + with open(liberation_scripting_path, "r") as libe_ms: + liberation_file_content = libe_ms.read() + + # Save original file + if current_file_content != liberation_file_content: + copyfile(mission_scripting_path, backup_scripting_path) + + # Replace DCS file + copyfile(liberation_scripting_path, mission_scripting_path) + + +def restore_original_mission_scripting(): + install_dir = get_dcs_install_directory() + mission_scripting_path = os.path.join(install_dir, "Scripts", "MissionScripting.lua") + backup_scripting_path = "./resources/scripts/MissionScripting.original.lua" + + if os.path.isfile(backup_scripting_path) and os.path.isfile(mission_scripting_path): + copyfile(backup_scripting_path, mission_scripting_path) + diff --git a/userdata/persistency.py b/userdata/persistency.py index 020bcf1f..fa833597 100644 --- a/userdata/persistency.py +++ b/userdata/persistency.py @@ -2,9 +2,6 @@ import logging import os import pickle import shutil -import sys - -from dcs import installation _dcs_saved_game_folder = None # type: str @@ -17,12 +14,7 @@ def setup(user_folder: str): def base_path() -> str: global _dcs_saved_game_folder assert _dcs_saved_game_folder - - openbeta_path = _dcs_saved_game_folder + ".openbeta" - if os.path.exists(openbeta_path): - return openbeta_path # For standalone openbeta users - else: - return _dcs_saved_game_folder # For standalone stable users & steam users (any branch) + return _dcs_saved_game_folder def _save_file() -> str: