1.2 update

..
This commit is contained in:
spencer-ki
2022-05-06 21:52:53 -07:00
parent 2ebafda806
commit 8ffaab1e6a
43 changed files with 1481 additions and 8393 deletions

View File

@@ -2,6 +2,7 @@ import json
import yaml
import sys
import os
import operator
import RotorOpsMission as ROps
import RotorOpsUnits
@@ -28,10 +29,18 @@ import qtmodern.windows
# UPDATE BUILD VERSION
maj_version = 1
minor_version = 1
patch_version = 2
minor_version = 2
patch_version = 0
modules_version = 2
modules_url = 'https://dcs-helicopters.com/user-files/modules/'
version_url = 'https://dcs-helicopters.com/app-updates/versions.yaml'
modules_map_url = 'https://dcs-helicopters.com/user-files/modules/module-map-v2.yaml'
ratings_url = 'https://dcs-helicopters.com/user-files/ratings.php'
allowed_paths = ['templates\\Scenarios\\downloaded', 'templates\\Forces\\downloaded', 'templates\\Imports\\downloaded']
user_files_url = 'https://dcs-helicopters.com/user-files/'
version_url = 'https://dcs-helicopters.com/app-updates/versioncheck.yaml'
#Setup logfile and exception handler
logger = logging.getLogger(__name__)
@@ -50,27 +59,39 @@ class directories:
os.chdir("..")
cls.home_dir = os.getcwd()
cls.scenarios = cls.home_dir + "\\templates\\Scenarios"
cls.forces = cls.home_dir + "\\templates\\Forces"
cls.forces_downloaded = cls.home_dir + "\\templates\\Forces\\downloaded"
cls.forces_user = cls.home_dir + "\\templates\\Forces\\user"
cls.scripts = cls.home_dir + "\\scripts"
cls.sound = cls.home_dir + "\\sound\\embedded"
cls.output = cls.home_dir + "\\MissionOutput"
cls.assets = cls.home_dir + "\\assets"
cls.imports = cls.home_dir + "\\templates\\Imports"
cls.imports_downloaded = cls.home_dir + "\\templates\\Imports\\downloaded"
cls.imports_user = cls.home_dir + "\\templates\\Imports\\user"
cls.user_datafile_path = cls.home_dir + "\\config\\user-data.yaml"
cls.scenarios_downloaded = cls.scenarios + "\\downloaded"
cls.scenarios_user = cls.scenarios + "\\user"
cls.default_config = cls.home_dir + '\\config\\default-config.yaml'
os.chdir(current_dir)
directories.find()
@classmethod
def createDirectories(cls):
required_dirs = [cls.scenarios_user, cls.scenarios_downloaded, cls.imports_user, cls.imports_downloaded, cls.forces_user, cls.forces_downloaded, cls.output]
for path in required_dirs:
if not os.path.exists(path):
os.makedirs(path)
import MissionGeneratorScenario
directories.find()
directories.createDirectories()
import MissionGeneratorTemplates
def handle_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt): #example of handling error subclasses
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
QApplication.restoreOverrideCursor()
logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
msg = QMessageBox()
msg.setWindowTitle("Uncaught exception")
@@ -82,9 +103,6 @@ sys.excepthook = handle_exception
version_string = str(maj_version) + "." + str(minor_version) + "." + str(patch_version)
# scenarios = []
red_forces_files = []
blue_forces_files = []
defenders_text = "Defending Forces:"
attackers_text = "Attacking Forces:"
ratings_json = None
@@ -120,16 +138,17 @@ class Window(QMainWindow, Ui_MainWindow):
self.player_slots = []
self.user_output_dir = None
self.user_data = None
self.forces_list = []
self.imports_list = []
self.user_data = self.loadUserData()
self.m = ROps.RotorOpsMission()
self.setupUi(self)
self.connectSignalsSlots()
self.populateScenarios()
self.populateForces("red", self.redforces_comboBox, red_forces_files)
self.populateForces("blue", self.blueforces_comboBox, blue_forces_files)
self.populateForces()
self.populateSlotSelection()
self.getImports()
# self.blue_forces_label.setText(attackers_text)
# self.red_forces_label.setText(defenders_text)
@@ -139,11 +158,15 @@ class Window(QMainWindow, Ui_MainWindow):
"QStatusBar{padding-left:5px;}")
self.version_label.setText("Version " + version_string)
self.scenarioChanged()
self.time_comboBox.addItem("Default Time")
self.time_comboBox.addItem("Day")
self.time_comboBox.addItem("Night")
self.time_comboBox.addItem("Dusk")
self.time_comboBox.addItem("Dawn")
self.time_comboBox.addItem("Noon")
self.time_comboBox.addItem("Random")
def connectSignalsSlots(self):
@@ -212,7 +235,7 @@ class Window(QMainWindow, Ui_MainWindow):
basename = filename.removesuffix('.miz')
mizpath = os.path.join(path, folder, filename)
# create scenario object
s = MissionGeneratorScenario.Scenario(mizpath, basename)
s = MissionGeneratorTemplates.Scenario(mizpath, basename)
#apply some properties if found in the downloads directory
if path == directories.scenarios_downloaded:
@@ -221,7 +244,6 @@ class Window(QMainWindow, Ui_MainWindow):
s.packageID = folder
if ratings_json:
print(ratings_json)
for module in ratings_json:
if module['package'] == folder:
s.rating = module["avg_rating"]
@@ -256,8 +278,8 @@ class Window(QMainWindow, Ui_MainWindow):
t_scenarios.append(s)
scenarios = t_scenarios.copy()
#self.scenario_comboBox.addItem(s.name)
self.scenarios_list = scenarios.copy()
self.scenarios_list = sorted(scenarios, key=lambda x: x.name, reverse=False)
for s in self.scenarios_list:
self.scenario_comboBox.addItem(s.name)
@@ -268,18 +290,67 @@ class Window(QMainWindow, Ui_MainWindow):
self.populateScenarios()
# self.scenarioChanged() haven't tried yet
def populateForces(self, side, combobox, files_list):
os.chdir(directories.home_dir)
# os.chdir(directories.forces + "/" + side)
os.chdir(directories.forces)
path = os.getcwd()
dir_list = os.listdir(path)
logger.info("Looking for " + side + " Forces files in '" + path)
def populateForces(self):
self.forces_list = []
for filename in dir_list:
if filename.endswith(".miz"):
files_list.append(filename)
combobox.addItem(filename.removesuffix('.miz'))
for path in [directories.forces_downloaded, directories.forces_user]:
logger.info("Looking for forces files in " + path)
os.chdir(path)
module_folders = next(os.walk('.'))[1]
for folder in module_folders:
for filename in os.listdir(folder):
if filename.endswith(".miz"):
basename = filename.removesuffix('.miz')
mizpath = os.path.join(path, folder, filename)
config_file_path = os.path.join(path, folder, basename + '.yaml')
if os.path.exists(config_file_path):
# create forces object with config
try:
config = yaml.safe_load(open(config_file_path))
f = MissionGeneratorTemplates.Forces(mizpath, filename, config)
self.forces_list.append(f)
except:
logger.error("Error in " + config_file_path)
else:
# create forces object without config
f = MissionGeneratorTemplates.Forces(mizpath, basename)
self.forces_list.append(f)
self.forces_list = sorted(self.forces_list, key=lambda x: x.name, reverse=False)
for forces in self.forces_list:
self.redforces_comboBox.addItem(forces.name)
self.blueforces_comboBox.addItem(forces.name)
def getImports(self):
self.imports_list = []
for path in [directories.imports_downloaded, directories.imports_user]:
logger.info("Looking for imports files in " + path)
os.chdir(path)
module_folders = next(os.walk('.'))[1]
for folder in module_folders:
for filename in os.listdir(folder):
if filename.endswith(".miz"):
basename = filename.removesuffix('.miz')
mizpath = os.path.join(path, folder, filename)
config_file_path = os.path.join(path, folder, basename + '.yaml')
if os.path.exists(config_file_path):
# create imports object with config
try:
config = yaml.safe_load(config_file_path)
f = MissionGeneratorTemplates.Import(mizpath, filename, config)
self.imports_list.append(f)
except:
logger.error("Error in " + config_file_path)
else:
# create imports object without config
f = MissionGeneratorTemplates.Import(mizpath, filename)
self.imports_list.append(f)
def populateSlotSelection(self):
self.slot_template_comboBox.addItem("Multiple Slots")
@@ -321,8 +392,7 @@ class Window(QMainWindow, Ui_MainWindow):
# reset some UI elements
self.defense_checkBox.setEnabled(True)
if self.lockedSlot():
self.slot_template_comboBox.removeItem(self.lockedSlot())
self.slot_template_comboBox.removeItem(self.lockedSlot())
self.slot_template_comboBox.setEnabled(True)
self.slot_template_comboBox.setCurrentIndex(0)
@@ -362,11 +432,14 @@ class Window(QMainWindow, Ui_MainWindow):
button.setEnabled(True)
if 'blue_forces' in config:
self.blueforces_comboBox.setCurrentIndex(self.blueforces_comboBox.findText(config['blue_forces']))
for template in self.forces_list:
if template.basename == config['blue_forces']:
self.blueforces_comboBox.setCurrentIndex(self.blueforces_comboBox.findText(template.name))
if 'red_forces' in config:
if self.redforces_comboBox.findText(config['red_forces']) >= 0:
self.redforces_comboBox.setCurrentIndex(self.redforces_comboBox.findText(config['red_forces']))
for template in self.forces_list:
if template.basename == config['red_forces']:
self.redforces_comboBox.setCurrentIndex(self.redforces_comboBox.findText(template.name))
except Exception as e:
logger.error("Error loading config file: " + str(e))
@@ -416,16 +489,16 @@ class Window(QMainWindow, Ui_MainWindow):
return
QApplication.setOverrideCursor(Qt.WaitCursor)
self.slot_template_comboBox.setCurrentIndex(0)
self.scenario = self.scenarios_list[self.scenario_comboBox.currentIndex()]
# reset generator options to default
default_config = self.loadScenarioConfig(directories.default_config)
self.applyScenarioConfig(default_config)
if self.scenario.config:
self.applyScenarioConfig(self.scenario.config)
self.m.setConfig(self.scenario.config)
else:
default_config = self.loadScenarioConfig(directories.default_config)
self.applyScenarioConfig(default_config)
self.m.setConfig(default_config)
path = self.scenario.path.removesuffix(".miz") + ".jpg"
if os.path.isfile(path):
@@ -462,17 +535,27 @@ class Window(QMainWindow, Ui_MainWindow):
def generateMissionAction(self):
QApplication.setOverrideCursor(Qt.WaitCursor)
red_forces_filename = red_forces_files[self.redforces_comboBox.currentIndex()]
blue_forces_filename = blue_forces_files[self.blueforces_comboBox.currentIndex()]
red_forces = self.forces_list[self.redforces_comboBox.currentIndex()]
blue_forces = self.forces_list[self.blueforces_comboBox.currentIndex()]
scenario_name = self.scenario.name
scenario_path = self.scenario.path
source = "offline"
credits = ("'" + scenario_name + "' mission template by " + self.scenario.author + "\n" +
"'" + red_forces.name + "' by " + red_forces.author + "\n" +
"'" + blue_forces.name + "' by " + blue_forces.author + "\n"
)
objects = {
"imports": self.imports_list,
}
data = {
"source": source,
"objects": objects,
"credits": credits,
"scenario_file": scenario_path,
"scenario_name": scenario_name,
"red_forces_filename": red_forces_filename,
"blue_forces_filename": blue_forces_filename,
"red_forces_path": red_forces.path,
"blue_forces_path": blue_forces.path,
"red_quantity": self.redqty_spinBox.value(),
"blue_quantity": self.blueqty_spinBox.value(),
"inf_spawn_qty": self.inf_spawn_spinBox.value(),
@@ -483,7 +566,7 @@ class Window(QMainWindow, Ui_MainWindow):
"f_awacs": self.awacs_checkBox.isChecked(),
"f_tankers": self.tankers_checkBox.isChecked(),
"voiceovers": self.voiceovers_checkBox.isChecked(),
"force_offroad": self.force_offroad_checkBox.isChecked(),
"force_offroad": self.scenario.getConfigValue("force_offroad", default=False),
"game_display": self.game_status_checkBox.isChecked(),
"defending": self.defense_checkBox.isChecked(),
"slots": self.slot_template_comboBox.currentText(),
@@ -494,6 +577,17 @@ class Window(QMainWindow, Ui_MainWindow):
"smoke_pickup_zones": self.smoke_pickup_zone_checkBox.isChecked(),
"player_slots": self.player_slots,
"player_hotstart": self.hotstart_checkBox.isChecked(),
"random_weather": self.random_weather_checkBox.isChecked(),
"time": self.time_comboBox.currentText(),
"start_trigger": self.scenario.getConfigValue("start_trigger", default=True),
"end_trigger": self.scenario.getConfigValue("end_trigger", default=True),
"farp_spawns": self.farp_spawn_checkBox.isChecked(),
"staging_logistics_file": self.scenario.getConfigValue("staging_logistics_file", default=None),
"zone_farp_file": self.scenario.getConfigValue("zone_farp_file", default=None),
"defensive_farp_file": self.scenario.getConfigValue("defensive_farp_file", default=None),
"logistics_farp_file": self.scenario.getConfigValue("logistics_farp_file", default=None),
"zone_protect_file": self.scenario.getConfigValue("zone_protect_file", default=None),
"script": self.scenario.getConfigValue("script", default=None),
}
logger.info("Generating mission with options:")
@@ -674,7 +768,7 @@ class Window(QMainWindow, Ui_MainWindow):
def checkVersion(splashscreen):
version_url = 'https://dcs-helicopters.com/app-updates/versioncheck.yaml'
try:
r = requests.get(version_url, allow_redirects=False, timeout=7)
v = yaml.safe_load(r.content)
@@ -693,31 +787,33 @@ def checkVersion(splashscreen):
modules_url = 'https://dcs-helicopters.com/user-files/modules/'
version_url = 'https://dcs-helicopters.com/app-updates/versions.yaml'
modules_map_url = 'https://dcs-helicopters.com/user-files/modules/module-map.yaml'
ratings_url = 'https://dcs-helicopters.com/user-files/ratings.php'
def loadModules(splashscreen):
msg = QMessageBox()
msg.setWindowTitle("Unable to connect to server")
msg.setText(
"We were unable to connect to the RotorOps server to download content. This is a temporary problem, so please try again later. If the problem persists, please get in touch via Discord.")
try:
r = requests.get(modules_map_url, allow_redirects=False, timeout=7)
if not r.status_code == 200:
logger.error("Could not retrieve the modules map.")
x = msg.exec_()
return
except:
logger.error("Failed to retrieve module map.")
x = msg.exec_()
return
module_list = yaml.safe_load(r.content)
files_success = []
files_failed = []
new_scenarios = []
updated_scenarios = []
new_modules = []
updated_modules = []
outversioned_modules = []
# Download scenarios files
#os.chdir(directories.scenarios)
if module_list:
for module in module_list:
@@ -725,15 +821,31 @@ def loadModules(splashscreen):
should_download = False
new_module = False
# only allow predefined paths
dp = module_list[module]["path"]
if dp not in allowed_paths:
logger.warning("Invalid path for module: " + module)
continue
# check if local version already exists
package_file_path = os.path.join(directories.scenarios_downloaded, module, "package.yaml")
package_file_path = os.path.join(directories.home_dir, module_list[module]["path"], module, "package.yaml")
if os.path.exists(package_file_path):
pkg_file = yaml.safe_load(open(package_file_path))
else:
pkg_file = None
#compare local and remote versions
# compare required generator version and actual version
if 'requires' in module_list[module]:
if module_list[module]['requires'] > modules_version:
name = 'unknown module'
if 'name' in module_list[module]:
name = module_list[module]['name']
outversioned_modules.append(name)
continue
# compare local and remote versions
if pkg_file and 'version' in pkg_file:
local_version = pkg_file['version']
@@ -744,6 +856,20 @@ def loadModules(splashscreen):
should_download = True
new_module = True
# delete modules with 'remove' dist property
if 'dist' in module_list[module] and module_list[module]['dist'] == 'remove':
for filename in module_list[module]["files"]:
module_dir = os.path.join(directories.home_dir, module_list[module]["path"], module)
file_path = os.path.join(module_dir, filename)
if os.path.exists(file_path):
try:
os.remove(file_path)
print("Removed module file: " + filename)
except:
logger.error("Error while trying to remove " + filename)
continue
# download files
if should_download:
logger.info("Updating module: " + module)
module_dir = os.path.join(directories.home_dir, module_list[module]["path"], module)
@@ -751,10 +877,11 @@ def loadModules(splashscreen):
# download files in remote package
for filename in module_list[module]["files"]:
broken_file = False
type_path = module_list[module]["type"]
splash.showMessage("Downloading " + filename + " ...", Qt.AlignHCenter | Qt.AlignTop, Qt.white)
app.processEvents()
url = modules_url + module + "/" + filename
url = modules_url + type_path + "/" + module + "/" + filename
try:
r = requests.get(url, allow_redirects=False, timeout=10)
except:
@@ -771,9 +898,9 @@ def loadModules(splashscreen):
# do some stuff for the dialog popup
if filename.endswith('.miz') and "name" in module_list[module]:
if new_module:
new_scenarios.append(module_list[module]["name"])
new_modules.append(module_list[module]["name"])
else:
updated_scenarios.append(module_list[module]["name"])
updated_modules.append(module_list[module]["name"])
else:
broken_file = True
files_failed.append(filename)
@@ -791,7 +918,7 @@ def loadModules(splashscreen):
logger.error("Problem encountered with modules map.")
# show a popup if we downloaded any packages
if len(files_success) > 0 or len(files_failed) > 0:
if len(files_success) > 0 or len(files_failed) > 0 or len(outversioned_modules) > 0:
if len(files_failed) > 0:
fs = ""
for filename in files_failed:
@@ -800,16 +927,18 @@ def loadModules(splashscreen):
msg = QMessageBox()
msg.setWindowTitle("Downloaded Files")
message = ""
if len(new_scenarios) > 0:
message = message + "New scenarios added: \n\n"
for name in new_scenarios:
if len(new_modules) > 0:
message = message + "New modules added: \n\n"
for name in new_modules:
message = message + name + "\n"
if len(updated_scenarios) > 0:
message = message + "\nScenarios updated: \n"
for name in updated_scenarios:
if len(updated_modules) > 0:
message = message + "\nModules updated: \n"
for name in updated_modules:
message = message + name + "\n"
if len(files_failed) > 0:
message = message + "\n\n" + str(len(files_failed)) + " files failed."
if len(outversioned_modules) > 0:
message = message + "\n\n" + str(len(outversioned_modules)) + " modules did not download because you need an required update."
msg.setText(message)
x = msg.exec_()
else: