From 806d0cc52fc9b787ba724e53b1c636edbdc42eb5 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Mon, 20 Nov 2023 18:19:41 +0100 Subject: [PATCH] Added ability for users to edit configuration during installation --- .gitignore | 4 + installer/olympus.iss | 425 +++++++++++++++++++++++++- scripts/python/build_configurator.bat | 4 + scripts/python/configurator.py | 67 ++++ scripts/python/configurator.spec | 44 +++ 5 files changed, 529 insertions(+), 15 deletions(-) create mode 100644 scripts/python/build_configurator.bat create mode 100644 scripts/python/configurator.py create mode 100644 scripts/python/configurator.spec diff --git a/.gitignore b/.gitignore index 2f1bffe6..9aa40bdb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ bin /scripts/old +/scripts/python/dist +/scripts/python/build +/scripts/python/venv .vs x64 core.user @@ -15,3 +18,4 @@ node_modules hgt /client/public/databases/units/old /client/plugins/databasemanager/index.js + diff --git a/installer/olympus.iss b/installer/olympus.iss index 04904a3b..55b94165 100644 --- a/installer/olympus.iss +++ b/installer/olympus.iss @@ -11,6 +11,7 @@ UninstallFilesDir={app}\Mods\Services\Olympus SetupIconFile="..\img\olympus.ico" DirExistsWarning=no AppendDefaultDirName=no +LicenseFile="..\LEGAL" [Messages] WizardSelectDir=Select the location of DCS's Saved Games folder @@ -24,22 +25,26 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{ [Files] ; NOTE: Don't use "Flags: ignoreversion" on any shared system files -Source: "..\scripts\OlympusHook.lua"; DestDir: "{app}\Scripts\Hooks"; Flags: ignoreversion +; Source: "..\scripts\OlympusHook.lua"; DestDir: "{app}\Scripts\Hooks"; Flags: ignoreversion Source: "..\olympus.json"; DestDir: "{app}\Mods\Services\Olympus"; Flags: onlyifdoesntexist -Source: "..\scripts\OlympusCommand.lua"; DestDir: "{app}\Mods\Services\Olympus\Scripts"; Flags: ignoreversion -Source: "..\scripts\unitPayloads.lua"; DestDir: "{app}\Mods\Services\Olympus\Scripts"; Flags: ignoreversion -Source: "..\scripts\templates.lua"; DestDir: "{app}\Mods\Services\Olympus\Scripts"; Flags: ignoreversion -Source: "..\scripts\mist.lua"; DestDir: "{app}\Mods\Services\Olympus\Scripts"; Flags: ignoreversion -Source: "..\mod\*"; DestDir: "{app}\Mods\Services\Olympus"; Flags: ignoreversion recursesubdirs; -Source: "..\bin\*.dll"; DestDir: "{app}\Mods\Services\Olympus\bin"; Flags: ignoreversion; -Source: "..\client\bin\*"; DestDir: "{app}\Mods\Services\Olympus\client\bin"; Flags: ignoreversion; -Source: "..\client\node_modules\*"; DestDir: "{app}\Mods\Services\Olympus\client\node_modules"; Flags: ignoreversion recursesubdirs; -Source: "..\client\public\*"; DestDir: "{app}\Mods\Services\Olympus\client\public"; Flags: ignoreversion recursesubdirs; -Source: "..\client\routes\*"; DestDir: "{app}\Mods\Services\Olympus\client\routes"; Flags: ignoreversion recursesubdirs; -Source: "..\client\views\*"; DestDir: "{app}\Mods\Services\Olympus\client\views"; Flags: ignoreversion recursesubdirs; -Source: "..\client\*.*"; DestDir: "{app}\Mods\Services\Olympus\client"; Flags: ignoreversion; -Source: "..\img\olympus.ico"; DestDir: "{app}\Mods\Services\Olympus\img"; Flags: ignoreversion; -Source: "{#nwjsFolder}\*.*"; DestDir: "{app}\Mods\Services\Olympus\client"; Flags: ignoreversion recursesubdirs; +; Source: "..\scripts\OlympusCommand.lua"; DestDir: "{app}\Mods\Services\Olympus\Scripts"; Flags: ignoreversion +; Source: "..\scripts\unitPayloads.lua"; DestDir: "{app}\Mods\Services\Olympus\Scripts"; Flags: ignoreversion +; Source: "..\scripts\templates.lua"; DestDir: "{app}\Mods\Services\Olympus\Scripts"; Flags: ignoreversion +; Source: "..\scripts\mist.lua"; DestDir: "{app}\Mods\Services\Olympus\Scripts"; Flags: ignoreversion +; Source: "..\mod\*"; DestDir: "{app}\Mods\Services\Olympus"; Flags: ignoreversion recursesubdirs; +; Source: "..\bin\*.dll"; DestDir: "{app}\Mods\Services\Olympus\bin"; Flags: ignoreversion; +; Source: "..\client\bin\*"; DestDir: "{app}\Mods\Services\Olympus\client\bin"; Flags: ignoreversion; +; Source: "..\client\node_modules\*"; DestDir: "{app}\Mods\Services\Olympus\client\node_modules"; Flags: ignoreversion recursesubdirs; +; Source: "..\client\public\*"; DestDir: "{app}\Mods\Services\Olympus\client\public"; Flags: ignoreversion recursesubdirs; +; Source: "..\client\routes\*"; DestDir: "{app}\Mods\Services\Olympus\client\routes"; Flags: ignoreversion recursesubdirs; +; Source: "..\client\views\*"; DestDir: "{app}\Mods\Services\Olympus\client\views"; Flags: ignoreversion recursesubdirs; +; Source: "..\client\*.*"; DestDir: "{app}\Mods\Services\Olympus\client"; Flags: ignoreversion; +; Source: "..\img\olympus.ico"; DestDir: "{app}\Mods\Services\Olympus\img"; Flags: ignoreversion; +; Source: "{#nwjsFolder}\*.*"; DestDir: "{app}\Mods\Services\Olympus\client"; Flags: ignoreversion recursesubdirs; +Source: "..\scripts\python\dist\configurator.exe"; DestDir: "{app}\Mods\Services\Olympus"; Flags: ignoreversion + +[Run] +Filename: "{app}\Mods\Services\Olympus\configurator.exe"; Parameters: -a {code:GetAddress} -c {code:GetClientPort} -b {code:GetBackendPort} -p {code:GetPassword} -bp {code:GetBluePassword} -rp {code:GetRedPassword} [Code] function NeedsAddPath(Param: string): boolean; @@ -68,3 +73,393 @@ ChangesEnvironment=yes [Icons] Name: "{userdesktop}\DCS Olympus Client"; Filename: "{app}\Mods\Services\Olympus\client\nw.exe"; Tasks: desktopicon; IconFilename: "{app}\Mods\Services\Olympus\img\olympus.ico" + +[Code] +var + lblLocalInstall: TLabel; + lblLocalInstallInstructions: TNewStaticText; + lblServerInstall: TLabel; + lblServerInstallInstructions: TNewStaticText; + lblClientPort: TLabel; + lblBackendPort: TLabel; + lblPassword: TLabel; + lblBluePassword: TLabel; + lblRedPassword: TLabel; + txtLocalInstall: TNewRadioButton; + txtServerInstall: TNewRadioButton; + txtClientPort: TEdit; + txtBackendPort: TEdit; + txtPassword: TPasswordEdit; + txtBluePassword: TPasswordEdit; + txtRedPassword: TPasswordEdit; + AddressPage: Integer; + PasswordPage: Integer; + lblPasswordInstructions: TNewStaticText; + +procedure AcceptNumbersOnlyKeyPress(Sender: TObject; var Key: Char); +var + KeyCode: Integer; +begin + // allow only numbers + KeyCode := Ord(Key); + if not ((KeyCode = 8) or ((KeyCode >= 48) and (KeyCode <= 57))) then + Key := #0; +end; + +procedure frmAddress_Activate(Page: TWizardPage); +begin +end; + +function frmAddress_ShouldSkipPage(Page: TWizardPage): Boolean; +begin + Result := False; +end; + +function frmAddress_BackButtonClick(Page: TWizardPage): Boolean; +begin + Result := True; +end; + +function frmAddress_NextButtonClick(Page: TWizardPage): Boolean; +begin + Result := True; +end; + +procedure frmAddress_CancelButtonClick(Page: TWizardPage; var Cancel, Confirm: Boolean); +begin +end; + +function frmAddress_CreatePage(PreviousPageId: Integer): Integer; +var + Page: TWizardPage; +begin + Page := CreateCustomPage( + PreviousPageId, + 'DCS Olympus configuration', + 'Setup DCS Olympus connectivity' + ); + + { lblLocalInstall } + lblLocalInstall := TLabel.Create(Page); + with lblLocalInstall do + begin + Parent := Page.Surface; + Left := ScaleX(30); + Top := ScaleY(14); + Width := ScaleX(35); + Height := ScaleY(10); + Font.Style := [fsBold]; + Caption := 'Local installation'; + end; + + { lblLocalInstallInstructions } + lblLocalInstallInstructions := TNewStaticText.Create(Page); + with lblLocalInstallInstructions do + begin + Parent := Page.Surface; + Left := ScaleX(30); + Top := ScaleY(31); + Width := ScaleX(340); + Height := ScaleY(23); + WordWrap := True; + Caption := 'Select this to install DCS Olympus locally. DCS Olympus will not be reachable by external clients (i.e. browsers running on different PCs)'; + end; + + { txtLocalInstall } + txtLocalInstall := TNewRadioButton.Create(Page); + with txtLocalInstall do + begin + Parent := Page.Surface; + Left := ScaleX(10); + Top := ScaleY(12); + Width := ScaleX(185); + Height := ScaleY(21); + TabOrder := 0; + Checked := True + end; + + { lblServerInstall } + lblServerInstall := TLabel.Create(Page); + with lblServerInstall do + begin + Parent := Page.Surface; + Left := ScaleX(30); + Top := ScaleY(76); + Width := ScaleX(52); + Height := ScaleY(13); + Font.Style := [fsBold]; + Caption := 'Dedicated server installation'; + end; + + { lblServerInstallInstructions } + lblServerInstallInstructions := TNewStaticText.Create(Page); + with lblServerInstallInstructions do + begin + Parent := Page.Surface; + Left := ScaleX(30); + Top := ScaleY(93); + Width := ScaleX(340); + Height := ScaleY(13); + WordWrap := True; + Caption := 'Select this to install DCS Olympus on a dedicated server. DCS Olympus will be reachable by external clients. NOTE: to enable external connections, port forwarding must be enabled. By default, ports 3000 and 30000 must be forwarded on TCP and UDP.'; + end; + + { txtServerInstall } + txtServerInstall := TNewRadioButton.Create(Page); + with txtServerInstall do + begin + Parent := Page.Surface; + Left := ScaleX(10); + Top := ScaleY(72); + Width := ScaleX(185); + Height := ScaleY(21); + TabOrder := 1; + end; + + { lblClientPort } + lblClientPort := TLabel.Create(Page); + with lblClientPort do + begin + Parent := Page.Surface; + Left := ScaleX(24); + Top := ScaleY(168); + Width := ScaleX(46); + Height := ScaleY(13); + Caption := 'Webserver port'; + end; + + { txtClientPort } + txtClientPort := TEdit.Create(Page); + with txtClientPort do + begin + Parent := Page.Surface; + Left := ScaleX(180); + Top := ScaleY(165); + Width := ScaleX(185); + Height := ScaleY(21); + Text := '3000'; + OnKeyPress := @AcceptNumbersOnlyKeyPress; + TabOrder := 3; + end; + + { lblBackendPort } + lblBackendPort := TLabel.Create(Page); + with lblBackendPort do + begin + Parent := Page.Surface; + Left := ScaleX(24); + Top := ScaleY(198); + Width := ScaleX(46); + Height := ScaleY(13); + Caption := 'DCS Olympus port'; + end; + + { txtBackendPort } + txtBackendPort := TEdit.Create(Page); + with txtBackendPort do + begin + Parent := Page.Surface; + Left := ScaleX(180); + Top := ScaleY(195); + Width := ScaleX(185); + Height := ScaleY(21); + Text := '3001'; + OnKeyPress := @AcceptNumbersOnlyKeyPress; + TabOrder := 4; + end; + + with Page do + begin + OnActivate := @frmAddress_Activate; + OnShouldSkipPage := @frmAddress_ShouldSkipPage; + OnBackButtonClick := @frmAddress_BackButtonClick; + OnNextButtonClick := @frmAddress_NextButtonClick; + OnCancelButtonClick := @frmAddress_CancelButtonClick; + end; + + Result := Page.ID; +end; + +procedure frmPassword_Activate(Page: TWizardPage); +begin +end; + +function frmPassword_ShouldSkipPage(Page: TWizardPage): Boolean; +begin + Result := False; +end; + +function frmPassword_BackButtonClick(Page: TWizardPage): Boolean; +begin + Result := True; +end; + +function frmPassword_NextButtonClick(Page: TWizardPage): Boolean; +begin + if (Trim(txtPassword.Text) <> '') and (Trim(txtBluePassword.Text) <> '') and (Trim(txtRedPassword.Text) <> '') then begin + Result := True; + end else + begin + MsgBox('All password fields must be filled to proceed.', mbInformation, MB_OK); + Result := False; + end; +end; + +procedure frmPassword_CancelButtonClick(Page: TWizardPage; var Cancel, Confirm: Boolean); +begin +end; + +function frmPassword_CreatePage(PreviousPageId: Integer): Integer; +var + Page: TWizardPage; +begin + Page := CreateCustomPage( + PreviousPageId, + 'DCS Olympus passwords', + 'Set DCS Olympus Admin and Commander passwords' + ); + + { lblPassword } + lblPassword := TLabel.Create(Page); + with lblPassword do + begin + Parent := Page.Surface; + Left := ScaleX(24); + Top := ScaleY(28); + Width := ScaleX(46); + Height := ScaleY(13); + Caption := 'Game Master password'; + end; + + { txtPassword } + txtPassword := TPasswordEdit.Create(Page); + with txtPassword do + begin + Parent := Page.Surface; + Left := ScaleX(180); + Top := ScaleY(25); + Width := ScaleX(185); + Height := ScaleY(21); + TabOrder := 2; + end; + + { lblBluePassword } + lblBluePassword := TLabel.Create(Page); + with lblBluePassword do + begin + Parent := Page.Surface; + Left := ScaleX(24); + Top := ScaleY(58); + Width := ScaleX(46); + Height := ScaleY(13); + Caption := 'Blue Commander password'; + end; + + { txtBluePassword } + txtBluePassword := TPasswordEdit.Create(Page); + with txtBluePassword do + begin + Parent := Page.Surface; + Left := ScaleX(180); + Top := ScaleY(55); + Width := ScaleX(185); + Height := ScaleY(21); + TabOrder := 2; + end; + + { lblRedPassword } + lblRedPassword := TLabel.Create(Page); + with lblRedPassword do + begin + Parent := Page.Surface; + Left := ScaleX(24); + Top := ScaleY(88); + Width := ScaleX(46); + Height := ScaleY(13); + Caption := 'Red Commander password'; + end; + + { txtRedPassword } + txtRedPassword := TPasswordEdit.Create(Page); + with txtRedPassword do + begin + Parent := Page.Surface; + Left := ScaleX(180); + Top := ScaleY(85); + Width := ScaleX(185); + Height := ScaleY(21); + TabOrder := 2; + end; + + + { lblPasswordInstructions } + lblPasswordInstructions := TNewStaticText.Create(Page); + with lblPasswordInstructions do + begin + Parent := Page.Surface; + Left := ScaleX(24); + Top := ScaleY(120); + Width := ScaleX(340); + Height := ScaleY(13); + WordWrap := True; + Caption := 'Passwords can be changed in the future by editing the file "olympus.json". For more information, see the DCS Olympus Wiki'; + end; + + with Page do + begin + OnActivate := @frmPassword_Activate; + OnShouldSkipPage := @frmPassword_ShouldSkipPage; + OnBackButtonClick := @frmPassword_BackButtonClick; + OnNextButtonClick := @frmPassword_NextButtonClick; + OnCancelButtonClick := @frmPassword_CancelButtonClick; + end; + + Result := Page.ID; +end; + +procedure InitializeWizard(); +begin + {this page will come after welcome page} + AddressPage := frmAddress_CreatePage(wpSelectDir); + PasswordPage:= frmPassword_CreatePage(AddressPage); +end; + +function GetAddress(Value: string): string; +begin + if txtLocalInstall.Checked then begin + Result := 'localhost' + end else + begin + Result := '*' + end +end; + +function GetClientPort(Value: string): string; +begin + Result := txtClientPort.Text; +end; + +function GetBackendPort(Value: string): string; +begin + Result := txtBackendPort.Text; +end; + +function GetPassword(Value: string): string; +begin + Result := txtPassword.Text; +end; + +function GetBluePassword(Value: string): string; +begin + Result := txtBluePassword.Text; +end; + +function GetRedPassword(Value: string): string; +begin + Result := txtRedPassword.Text; +end; + + + + + diff --git a/scripts/python/build_configurator.bat b/scripts/python/build_configurator.bat new file mode 100644 index 00000000..60934864 --- /dev/null +++ b/scripts/python/build_configurator.bat @@ -0,0 +1,4 @@ +python -m venv venv +call ./venv/Scripts/activate +pip install pyinstaller +python -m PyInstaller configurator.py --onefile \ No newline at end of file diff --git a/scripts/python/configurator.py b/scripts/python/configurator.py new file mode 100644 index 00000000..3fcbcda3 --- /dev/null +++ b/scripts/python/configurator.py @@ -0,0 +1,67 @@ +import argparse, json + +def main(): + parser = argparse.ArgumentParser( + prog='DCS Olympus configurator', + description='This software allows to edit the DCS Olympus configuration file', + epilog='') + + parser.add_argument('-a', '--address') + parser.add_argument('-c', '--clientPort') + parser.add_argument('-b', '--backendPort') + parser.add_argument('-p', '--password') + parser.add_argument('-bp', '--bluePassword') + parser.add_argument('-rp', '--redPassword') + + args = parser.parse_args() + + with open("olympus.json", "r") as fp: + config = json.load(fp) + + if (args.address is not None): + config["server"]["address"] = args.address + print(f"Address set to {args.address}") + else: + print("No address provided, skipping...") + + if args.backendPort is not None: + if args.backendPort.isdecimal(): + config["server"]["port"] = int(args.backendPort) + print(f"Backend port set to {args.backendPort}") + else: + print(f"Invalid backend port provided {args.backendPort}") + else: + print("No backend port provided, skipping...") + + if (args.password is not None): + config["authentication"]["gameMasterPassword"] = args.password + print(f"Game Master password set to {args.password}") + else: + print("No Game Master password provided, skipping...") + + if (args.bluePassword is not None): + config["authentication"]["blueCommanderPassword"] = args.bluePassword + print(f"Blue Commander password set to {args.bluePassword}") + else: + print("No Blue Commander password provided, skipping...") + + if (args.redPassword is not None): + config["authentication"]["redCommanderPassword"] = args.redPassword + print(f"Red Commander password set to {args.redPassword}") + else: + print("No Red Commander password provided, skipping...") + + if args.clientPort is not None: + if args.clientPort.isdecimal(): + config["client"]["port"] = int(args.clientPort) + print(f"Client port set to {args.clientPort}") + else: + print(f"Invalid client port provided {args.clientPort}") + else: + print("No client port provided, skipping...") + + with open("olympus.json", "w") as fp: + json.dump(config, fp, indent = 4) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/python/configurator.spec b/scripts/python/configurator.spec new file mode 100644 index 00000000..70674949 --- /dev/null +++ b/scripts/python/configurator.spec @@ -0,0 +1,44 @@ +# -*- mode: python ; coding: utf-8 -*- + + +block_cipher = None + + +a = Analysis( + ['configurator.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='configurator', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +)