Skip to content

Commit 092b897

Browse files
committed
installer: add Export.lua installation to setup wizard and reorganize menu
- Add ExportLuaInstaller class for copying Export.lua to DCS Scripts folder - Add version detection from STREAMER_VERSION variable - Show current and new version when overwriting existing Export.lua - Create automatic backup before overwriting - Add ExportLuaInstallPage to setup wizard - Move Setup Wizard from File menu to separate menu item - Remove QR Code scan button (server only generates QR codes) - Update port configuration to 5011 for consistency
1 parent 017fe74 commit 092b897

File tree

2 files changed

+262
-20
lines changed

2 files changed

+262
-20
lines changed

scripts/DCS-SCRIPTS-FOLDER-Experimental/dcs_datapad_installer.py

Lines changed: 261 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,85 @@ def check_firewall() -> Tuple[bool, str]:
432432
return False, f"Could not check firewall: {str(e)}"
433433

434434

435+
class ExportLuaInstaller:
436+
"""Helper class for Export.lua installation"""
437+
438+
@staticmethod
439+
def extract_version(lua_file_path: Path) -> Optional[str]:
440+
"""Extract STREAMER_VERSION from Export.lua"""
441+
try:
442+
if not lua_file_path.exists():
443+
return None
444+
445+
with open(lua_file_path, 'r', encoding='utf-8') as f:
446+
for line in f:
447+
# Look for: local STREAMER_VERSION = "1.0.13"
448+
if 'STREAMER_VERSION' in line and '=' in line:
449+
# Extract version string
450+
parts = line.split('=')
451+
if len(parts) >= 2:
452+
version_part = parts[1].strip()
453+
# Remove quotes and comments
454+
version = version_part.strip('"').strip("'").split('--')[0].strip()
455+
return version
456+
return None
457+
except Exception as e:
458+
logger.error(f"Error extracting version from {lua_file_path}: {e}")
459+
return None
460+
461+
@staticmethod
462+
def copy_export_lua(source_path: Path, dest_dir: Path, parent_widget=None) -> Tuple[bool, str]:
463+
"""
464+
Copy Export.lua to DCS Scripts folder
465+
Returns (success, message)
466+
"""
467+
try:
468+
# Ensure destination directory exists
469+
dest_dir.mkdir(parents=True, exist_ok=True)
470+
dest_file = dest_dir / "Export.lua"
471+
472+
# Get source version
473+
source_version = ExportLuaInstaller.extract_version(source_path)
474+
source_version_str = source_version if source_version else "Unknown"
475+
476+
# Check if destination file exists
477+
if dest_file.exists():
478+
# Get existing version
479+
existing_version = ExportLuaInstaller.extract_version(dest_file)
480+
existing_version_str = existing_version if existing_version else "Unknown"
481+
482+
# Ask user for confirmation
483+
if parent_widget:
484+
reply = QMessageBox.question(
485+
parent_widget,
486+
"Export.lua Already Exists",
487+
f"Export.lua already exists in the DCS Scripts folder.\n\n"
488+
f"<b>Current Version:</b> {existing_version_str}\n"
489+
f"<b>New Version:</b> {source_version_str}\n\n"
490+
f"Do you want to overwrite the existing file?",
491+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
492+
)
493+
494+
if reply == QMessageBox.StandardButton.No:
495+
return False, "Installation cancelled by user"
496+
497+
# Create backup of existing file
498+
if dest_file.exists():
499+
backup_file = dest_file.with_suffix('.lua.backup')
500+
shutil.copy2(dest_file, backup_file)
501+
logger.info(f"Created backup: {backup_file}")
502+
503+
# Copy the file
504+
shutil.copy2(source_path, dest_file)
505+
logger.info(f"Copied Export.lua from {source_path} to {dest_file}")
506+
507+
return True, f"Export.lua v{source_version_str} installed successfully"
508+
509+
except Exception as e:
510+
logger.error(f"Error copying Export.lua: {e}")
511+
return False, f"Failed to copy Export.lua: {str(e)}"
512+
513+
435514
class SetupWizard(QWizard):
436515
"""First-time setup wizard"""
437516

@@ -444,6 +523,7 @@ def __init__(self, parent=None):
444523
# Add pages
445524
self.addPage(WelcomePage())
446525
self.addPage(DCSDetectionPage())
526+
self.addPage(ExportLuaInstallPage())
447527
self.addPage(DependencyInstallPage())
448528
self.addPage(ServerConfigPage())
449529
self.addPage(CompletionPage())
@@ -467,6 +547,7 @@ def __init__(self):
467547
"<p>This setup wizard will help you:</p>"
468548
"<ul>"
469549
"<li>Detect your DCS World installation</li>"
550+
"<li>Install Export.lua to DCS Scripts folder</li>"
470551
"<li>Install required Python dependencies</li>"
471552
"<li>Configure server settings</li>"
472553
"<li>Add your first authorized device</li>"
@@ -676,6 +757,179 @@ def __init__(self):
676757
self.registerField("pow_difficulty", self.pow_difficulty_spin)
677758

678759

760+
class ExportLuaInstallPage(QWizardPage):
761+
"""Export.lua installation page"""
762+
763+
def __init__(self):
764+
super().__init__()
765+
self.setTitle("Install Export.lua")
766+
self.setSubTitle("Copy Export.lua to DCS Scripts folder")
767+
768+
layout = QVBoxLayout()
769+
770+
# Info section
771+
info_label = QLabel(
772+
"<p>The Export.lua file is required for DCS to send flight data to the DataPad server.</p>"
773+
"<p>This step will copy Export.lua from the installation folder to your DCS Scripts folder.</p>"
774+
)
775+
info_label.setWordWrap(True)
776+
layout.addWidget(info_label)
777+
778+
# Source file info
779+
source_group = QGroupBox("Source File")
780+
source_layout = QFormLayout()
781+
782+
self.source_path_label = QLabel("Searching...")
783+
source_layout.addRow("Export.lua Location:", self.source_path_label)
784+
785+
self.source_version_label = QLabel("Unknown")
786+
source_layout.addRow("Version:", self.source_version_label)
787+
788+
source_group.setLayout(source_layout)
789+
layout.addWidget(source_group)
790+
791+
# Destination info
792+
dest_group = QGroupBox("Destination")
793+
dest_layout = QFormLayout()
794+
795+
self.dest_path_label = QLabel("Will be determined from DCS installation")
796+
dest_layout.addRow("Scripts Folder:", self.dest_path_label)
797+
798+
self.existing_version_label = QLabel("Not installed")
799+
dest_layout.addRow("Current Version:", self.existing_version_label)
800+
801+
dest_group.setLayout(dest_layout)
802+
layout.addWidget(dest_group)
803+
804+
# Install button and status
805+
button_layout = QHBoxLayout()
806+
807+
self.install_btn = QPushButton("📄 Install Export.lua")
808+
self.install_btn.setMinimumHeight(40)
809+
self.install_btn.clicked.connect(self.install_export_lua)
810+
button_layout.addWidget(self.install_btn)
811+
812+
self.skip_btn = QPushButton("⏭ Skip Installation")
813+
self.skip_btn.setMinimumHeight(40)
814+
self.skip_btn.clicked.connect(self.skip_installation)
815+
button_layout.addWidget(self.skip_btn)
816+
817+
layout.addLayout(button_layout)
818+
819+
# Status label
820+
self.status_label = QLabel("")
821+
self.status_label.setWordWrap(True)
822+
layout.addWidget(self.status_label)
823+
824+
layout.addStretch()
825+
826+
self.setLayout(layout)
827+
828+
# Track installation status
829+
self.installed = False
830+
831+
def initializePage(self):
832+
"""Initialize page when shown"""
833+
self.detect_export_lua()
834+
self.update_destination_info()
835+
836+
def detect_export_lua(self):
837+
"""Detect Export.lua in current directory"""
838+
# Look for Export.lua in current directory
839+
current_dir = Path.cwd()
840+
export_lua_path = current_dir / "Export.lua"
841+
842+
if export_lua_path.exists():
843+
self.source_path_label.setText(str(export_lua_path))
844+
version = ExportLuaInstaller.extract_version(export_lua_path)
845+
self.source_version_label.setText(version if version else "Unknown")
846+
self.source_version_label.setStyleSheet("color: green; font-weight: bold;")
847+
else:
848+
self.source_path_label.setText("❌ Not found in current directory")
849+
self.source_path_label.setStyleSheet("color: red;")
850+
self.source_version_label.setText("N/A")
851+
self.install_btn.setEnabled(False)
852+
self.status_label.setText(
853+
"⚠ Warning: Export.lua not found in the current directory.\n"
854+
"Please make sure Export.lua is in the same folder as this installer."
855+
)
856+
self.status_label.setStyleSheet("color: orange; font-weight: bold;")
857+
858+
def update_destination_info(self):
859+
"""Update destination path and existing version info"""
860+
# Get saved games path from wizard
861+
wizard = self.wizard()
862+
if hasattr(wizard, 'field'):
863+
dcs_path = wizard.field("dcs_path")
864+
if dcs_path:
865+
# Try to detect saved games from DCS installation
866+
saved_games_path = DCSPathDetector.detect_saved_games()
867+
if saved_games_path:
868+
scripts_folder = Path(saved_games_path) / "Scripts"
869+
self.dest_path_label.setText(str(scripts_folder))
870+
871+
# Check if Export.lua already exists
872+
existing_export = scripts_folder / "Export.lua"
873+
if existing_export.exists():
874+
existing_version = ExportLuaInstaller.extract_version(existing_export)
875+
self.existing_version_label.setText(existing_version if existing_version else "Unknown")
876+
self.existing_version_label.setStyleSheet("color: blue; font-weight: bold;")
877+
else:
878+
self.existing_version_label.setText("Not installed")
879+
self.existing_version_label.setStyleSheet("color: gray;")
880+
return
881+
882+
self.dest_path_label.setText("⚠ DCS Saved Games folder not detected")
883+
self.dest_path_label.setStyleSheet("color: orange;")
884+
885+
def install_export_lua(self):
886+
"""Install Export.lua to DCS Scripts folder"""
887+
try:
888+
# Get paths
889+
source_path = Path.cwd() / "Export.lua"
890+
if not source_path.exists():
891+
self.status_label.setText("❌ Error: Export.lua not found in current directory")
892+
self.status_label.setStyleSheet("color: red; font-weight: bold;")
893+
return
894+
895+
saved_games_path = DCSPathDetector.detect_saved_games()
896+
if not saved_games_path:
897+
self.status_label.setText("❌ Error: Could not detect DCS Saved Games folder")
898+
self.status_label.setStyleSheet("color: red; font-weight: bold;")
899+
return
900+
901+
scripts_folder = Path(saved_games_path) / "Scripts"
902+
903+
# Perform installation
904+
success, message = ExportLuaInstaller.copy_export_lua(
905+
source_path,
906+
scripts_folder,
907+
parent_widget=self
908+
)
909+
910+
if success:
911+
self.status_label.setText(f"✅ {message}")
912+
self.status_label.setStyleSheet("color: green; font-weight: bold;")
913+
self.installed = True
914+
self.install_btn.setEnabled(False)
915+
self.install_btn.setText("✅ Installed")
916+
# Update existing version display
917+
self.update_destination_info()
918+
else:
919+
self.status_label.setText(f"ℹ {message}")
920+
self.status_label.setStyleSheet("color: gray; font-weight: bold;")
921+
922+
except Exception as e:
923+
logger.error(f"Error in install_export_lua: {e}")
924+
self.status_label.setText(f"❌ Error: {str(e)}")
925+
self.status_label.setStyleSheet("color: red; font-weight: bold;")
926+
927+
def skip_installation(self):
928+
"""Skip Export.lua installation"""
929+
self.status_label.setText("ℹ Installation skipped - you can install Export.lua manually later")
930+
self.status_label.setStyleSheet("color: gray;")
931+
932+
679933
class CompletionPage(QWizardPage):
680934
"""Setup completion page"""
681935

@@ -784,12 +1038,6 @@ def create_menu_bar(self):
7841038
# File menu
7851039
file_menu = menu_bar.addMenu("&File")
7861040

787-
run_wizard_action = QAction("Run Setup &Wizard", self)
788-
run_wizard_action.triggered.connect(self.show_setup_wizard)
789-
file_menu.addAction(run_wizard_action)
790-
791-
file_menu.addSeparator()
792-
7931041
exit_action = QAction("E&xit", self)
7941042
exit_action.triggered.connect(self.close)
7951043
file_menu.addAction(exit_action)
@@ -811,6 +1059,13 @@ def create_menu_bar(self):
8111059
restart_action.triggered.connect(self.restart_server)
8121060
server_menu.addAction(restart_action)
8131061

1062+
# Setup Wizard menu
1063+
wizard_menu = menu_bar.addMenu("Setup &Wizard")
1064+
1065+
run_wizard_action = QAction("Run Setup &Wizard", self)
1066+
run_wizard_action.triggered.connect(self.show_setup_wizard)
1067+
wizard_menu.addAction(run_wizard_action)
1068+
8141069
# Help menu
8151070
help_menu = menu_bar.addMenu("&Help")
8161071

@@ -898,10 +1153,6 @@ def create_device_management_tab(self):
8981153
remove_btn.clicked.connect(self.remove_selected_device)
8991154
toolbar.addWidget(remove_btn)
9001155

901-
qr_btn = QPushButton("📷 Scan QR Code")
902-
qr_btn.clicked.connect(self.scan_qr_code)
903-
toolbar.addWidget(qr_btn)
904-
9051156
generate_token_btn = QPushButton("🎫 Generate Token")
9061157
generate_token_btn.clicked.connect(self.generate_registration_token)
9071158
toolbar.addWidget(generate_token_btn)
@@ -1542,15 +1793,6 @@ def remove_selected_device(self):
15421793
else:
15431794
QMessageBox.warning(self, "Error", "Failed to remove device.")
15441795

1545-
def scan_qr_code(self):
1546-
"""Scan QR code for device registration (placeholder)"""
1547-
QMessageBox.information(
1548-
self,
1549-
"QR Code Scanner",
1550-
"QR Code scanning will be implemented in the next version.\n\n"
1551-
"For now, please use 'Generate Token' to create a registration token."
1552-
)
1553-
15541796
def generate_registration_token(self):
15551797
"""Generate a registration token and display QR code"""
15561798
try:

scripts/DCS-SCRIPTS-FOLDER-Experimental/server_config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"last_mode": 1,
33
"ip_address": "192.168.178.100",
44
"target_ip": "192.168.178.*",
5-
"port": 5010,
5+
"port": 5011,
66
"handshake_port": 5011,
77
"interval": 10,
88
"authorized_devices": "authorized_devices.json",

0 commit comments

Comments
 (0)