diff --git a/.github/workflows/release-2.1.1.0.yml b/.github/workflows/release-2.1.1.0.yml index 5ec16db..7e279f6 100644 --- a/.github/workflows/release-2.1.1.0.yml +++ b/.github/workflows/release-2.1.1.0.yml @@ -1,4 +1,4 @@ -name: Release HelperProfiles 2.1.1.0 +name: Build HelperProfiles 2.1.1.0 on: workflow_dispatch: @@ -7,80 +7,61 @@ on: - main paths: - .github/workflows/release-2.1.1.0.yml + - modDesc.xml + - docs/releases/2.1.1.0.md + - scripts/HP_AutoDriveContinuity.lua + - scripts/HP_AutoDrivePayrollBridge.lua + - scripts/HP_HelperAcquisitionRouter.lua + - scripts/HP_IntegrationAPI.lua permissions: contents: write jobs: - release: + package: runs-on: ubuntu-latest steps: - name: Check out repository uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Repair stable release metadata + - name: Validate ModHub package inputs shell: bash run: | set -euo pipefail python - <<'PY' from pathlib import Path - import re import xml.etree.ElementTree as ET - path = Path('modDesc.xml') - text = path.read_text(encoding='utf-8') - before, marker, changelog = text.partition(' ') - if not marker: - raise SystemExit('Missing changelog section') - - german_lines = [ - 'Version 2.1.1.0:', - '- Textur-Atlas-Artefakte und Layout-Darstellungsfehler in den Ansichten Erscheinungsbilder und Helferliste wurden behoben.', - '- Integration API v7 wurde hinzugefügt; der bestehende Helferlisten- und Identitätsvertrag bleibt erhalten.', - '- Kompatible Mods wie Remote Dispatcher können einen Helfer vorübergehend und gezielt für einen einzelnen Einstellvorgang anfordern.', - '- Gezielte Anforderungen schlagen kontrolliert fehl, wenn ein Helfer fehlt, nicht im Kader ist oder bereits aktiv ist; es wird kein anderer Helfer stillschweigend eingesetzt.', - '- Gezielte Anforderungen ändern weder den normal ausgewählten Helfer noch den Einstellungsmodus von HelperProfiles.', - '' + xml_files = [ + 'modDesc.xml', + 'gui/HP_AppearanceBindingsScreen.xml', + 'gui/HP_RosterManagerScreen.xml', + 'gui/guiProfiles.xml', + 'l10n/l10n_en.xml', + 'l10n/l10n_de.xml', + 'l10n/l10n_fr.xml', ] - german_block = '\n'.join(german_lines) - - # Remove the accidentally inserted German 2.1.1.0 block from EN. - changelog = changelog.replace(german_block, '', 1) - - # Add it to the German changelog if it is not already present there. - de_match = re.search(r'', changelog, flags=re.S) - if de_match is None: - raise SystemExit('Missing German changelog') - de_body = de_match.group(1) - if 'Textur-Atlas-Artefakte' not in de_body: - replacement = '' - changelog = changelog[:de_match.start()] + replacement + changelog[de_match.end():] - - text = before + marker + changelog - path.write_text(text, encoding='utf-8') - - compat = Path('scripts/HP_Compatibility.lua') - ctext = compat.read_text(encoding='utf-8') - ctext = ctext.replace( - 'caused the 2.1.1.0 alpha to scan every loaded mod on every frame.', - 'caused the 2.1.0.0 alpha to scan every loaded mod on every frame.' - ) - compat.write_text(ctext, encoding='utf-8') + for path in xml_files: + ET.parse(path) root = ET.parse('modDesc.xml').getroot() if root.findtext('version') != '2.1.1.0': raise SystemExit('Unexpected modDesc version') - repaired = path.read_text(encoding='utf-8') - _, _, repaired_changelog = repaired.partition(' ') - en = re.search(r'', repaired_changelog, flags=re.S).group(1) - de = re.search(r'', repaired_changelog, flags=re.S).group(1) - if 'Textur-Atlas-Artefakte' in en: - raise SystemExit('German release notes still present in English changelog') - if not de.lstrip().startswith('Version 2.1.1.0:'): - raise SystemExit('German changelog does not start with 2.1.1.0') + mod_desc = Path('modDesc.xml').read_text(encoding='utf-8') + if 'Fixed helper continuity with AutoDrive' not in mod_desc: + raise SystemExit('Missing AutoDrive continuity changelog entry') + + required_files = [ + 'icon_helperProfiles.dds', + 'scripts/HP_AutoDriveContinuity.lua', + 'scripts/HP_AutoDrivePayrollBridge.lua', + 'scripts/HP_HelperAcquisitionRouter.lua', + 'scripts/HP_IntegrationAPI.lua', + ] + for path in required_files: + if not Path(path).is_file(): + raise SystemExit('Missing required package file: ' + path) api = Path('scripts/HP_IntegrationAPI.lua').read_text(encoding='utf-8') for wanted in ['apiVersion = 7', 'modVersion = "2.1.1.0"', 'supportsScopedPreferredHire = true']: @@ -88,29 +69,6 @@ jobs: raise SystemExit('Missing API v7 marker: ' + wanted) PY - - name: Commit metadata corrections - shell: bash - run: | - set -euo pipefail - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add modDesc.xml scripts/HP_Compatibility.lua - if ! git diff --cached --quiet; then - git commit -m "Correct HelperProfiles 2.1.1.0 release metadata" - git push origin HEAD:main - fi - - - name: Validate package inputs - shell: bash - run: | - set -euo pipefail - test -f docs/releases/2.1.1.0.md - python - <<'PY' - import xml.etree.ElementTree as ET - for path in ['modDesc.xml', 'gui/HP_AppearanceBindingsScreen.xml', 'gui/HP_RosterManagerScreen.xml', 'gui/guiProfiles.xml']: - ET.parse(path) - PY - - name: Build Farming Simulator mod package shell: bash run: | @@ -120,24 +78,46 @@ jobs: python - <<'PY' import zipfile import xml.etree.ElementTree as ET + with zipfile.ZipFile('FS25_HelperProfiles.zip', 'r') as archive: names = set(archive.namelist()) required = { - 'modDesc.xml', 'icon_helperProfiles.dds', - 'gui/HP_AppearanceBindingsScreen.xml', 'gui/HP_RosterManagerScreen.xml', - 'scripts/HelperProfiles.lua', 'scripts/HP_RosterState.lua', - 'scripts/HP_RosterFilter.lua', 'scripts/HP_RosterManagerScreen.lua', - 'scripts/HP_TabbedManagement.lua', 'scripts/HP_IntegrationAPI.lua' + 'modDesc.xml', + 'icon_helperProfiles.dds', + 'gui/HP_AppearanceBindingsScreen.xml', + 'gui/HP_RosterManagerScreen.xml', + 'scripts/HelperProfiles.lua', + 'scripts/HP_RosterState.lua', + 'scripts/HP_RosterFilter.lua', + 'scripts/HP_RosterManagerScreen.lua', + 'scripts/HP_TabbedManagement.lua', + 'scripts/HP_IntegrationAPI.lua', + 'scripts/HP_AutoDriveContinuity.lua', + 'scripts/HP_AutoDrivePayrollBridge.lua', + 'scripts/HP_HelperAcquisitionRouter.lua', } missing = sorted(required - names) if missing: raise SystemExit('Missing required package files: ' + ', '.join(missing)) + + if any(name.startswith('FS25_HelperProfiles/') for name in names): + raise SystemExit('Package contains an unexpected wrapper directory') + root = ET.fromstring(archive.read('modDesc.xml')) if root.findtext('version') != '2.1.1.0': raise SystemExit('Unexpected packaged version') PY + - name: Upload ModHub-ready package artifact + uses: actions/upload-artifact@v4 + with: + name: FS25_HelperProfiles-2.1.1.0-ModHub + path: FS25_HelperProfiles.zip + if-no-files-found: error + retention-days: 7 + - name: Publish or refresh GitHub release + if: github.event_name == 'workflow_dispatch' shell: bash env: GH_TOKEN: ${{ github.token }} diff --git a/docs/releases/2.1.1.0.md b/docs/releases/2.1.1.0.md index b9a2648..6f29ca8 100644 --- a/docs/releases/2.1.1.0.md +++ b/docs/releases/2.1.1.0.md @@ -9,6 +9,8 @@ HelperProfiles 2.1.1.0 is a stable maintenance and integration update for the va - Added temporary **scoped preferred-worker hiring** for compatible companion mods such as Remote Dispatcher. - Scoped worker requests are fail-closed: a missing, OFF-roster or already-active worker is rejected rather than silently replaced by another worker. - Scoped requests do not change the worker selected in the normal HelperProfiles overlay and do not change the user's HelperProfiles hiring mode. +- Fixed **AutoDrive helper continuity** so an assigned worker is retained when AutoDrive temporarily releases and reacquires a helper during the same logical task. +- Reconciled scoped companion-mod hiring with AutoDrive continuity so an explicitly requested worker is used for the initial hire and remains the AutoDrive worker through subsequent internal reacquisition cycles. ## Remote Dispatcher integration @@ -16,11 +18,18 @@ Remote Dispatcher can use API v7 to bind a prepared vehicle to a specific Helper Remote Dispatcher remains optional; HelperProfiles does not require it. +## AutoDrive compatibility + +HelperProfiles now preserves the worker already assigned to an AutoDrive vehicle when AutoDrive performs a temporary helper release/reacquire cycle. A genuine AutoDrive stop still releases that worker normally, allowing them to return to the available roster. + +AutoDrive remains optional; HelperProfiles continues to work normally without it. + ## Compatibility - Single-player only. - AvatarSwitcher remains optional. - HelperPayroll remains optional. +- AutoDrive remains optional. - Hired Helper Tool remains incompatible because it also owns the helper roster. - Existing HelperProfiles 2.1.0.0 save data and per-save roster/appearance files remain compatible. diff --git a/modDesc.xml b/modDesc.xml index 8f87212..255483d 100644 --- a/modDesc.xml +++ b/modDesc.xml @@ -1,5 +1,5 @@ - + SimGamerJen 2.1.1.0 @@ -42,6 +42,7 @@ Not compatible with Hired Helper Tool or conflicting helper-roster expansion mod Changelog 2.1.1.0: - Fixed texture and layout issues in the Helper Management appearance and roster screens. - Added support for compatible companion mods to request a specific available worker without changing your selected worker or hiring mode. +- Fixed helper continuity with AutoDrive so an assigned worker is retained when AutoDrive temporarily releases and reacquires a helper. ]]></en> <de><![CDATA[ Stelle ein festes Team für deinen Hof zusammen und bestimme, wer den nächsten KI-Auftrag übernimmt. Helferprofile erweitert die standardmäßige Helferliste auf 20 feste Helferplätze (A-T) und bietet eine übersichtliche Statusanzeige sowie eine Helferverwaltung mit zwei Tabs. @@ -77,6 +78,7 @@ Nicht kompatibel mit Hired Helper Tool oder kollidierenden Mods zur Erweiterung Changelog 2.1.1.0: - Textur- und Layoutfehler in den Ansichten für Erscheinungsbilder und Helferliste der Helferverwaltung wurden behoben. - Kompatible Begleitmods können gezielt einen verfügbaren Helfer anfordern, ohne den ausgewählten Helfer oder den Einstellungsmodus zu ändern. +- Die Helfer-Kontinuität mit AutoDrive wurde behoben, sodass ein zugewiesener Helfer beibehalten wird, wenn AutoDrive ihn vorübergehend freigibt und erneut anfordert. ]]></de> <fr><![CDATA[ Constituez une équipe permanente pour votre ferme et choisissez qui effectuera la prochaine tâche d'IA. Profils d'aides étend la liste standard à 20 emplacements permanents (A-T), avec un tableau d'état clair et un écran de gestion à deux onglets. @@ -112,6 +114,7 @@ Non compatible avec Hired Helper Tool ou les mods d'extension de la liste des ai Changelog 2.1.1.0 : - Correction des problèmes de textures et de mise en page dans les écrans Apparences et Liste des aides. - Les mods complémentaires compatibles peuvent demander une aide disponible précise sans modifier l'aide sélectionnée ni le mode d'embauche. +- Correction de la continuité des aides avec AutoDrive afin qu'une aide affectée soit conservée lorsqu'AutoDrive la libère temporairement puis la réaffecte. ]]></fr> </description> @@ -148,6 +151,7 @@ Changelog 2.1.1.0 : <multiplayer supported="false"/> <extraSourceFiles> + <sourceFile filename="scripts/HP_ProtectedCall.lua"/> <sourceFile filename="scripts/HP_SlotRegistry.lua"/> <sourceFile filename="scripts/HP_Compatibility.lua"/> <sourceFile filename="scripts/HP_Config.lua"/> @@ -165,4 +169,4 @@ Changelog 2.1.1.0 : <sourceFile filename="scripts/HP_HelperAcquisitionRouter.lua"/> <sourceFile filename="scripts/RegisterPlayerActionEvents.lua"/> </extraSourceFiles> -</modDesc> \ No newline at end of file +</modDesc> diff --git a/scripts/HP_ASBridge.lua b/scripts/HP_ASBridge.lua index b4d2992..db4aec3 100644 --- a/scripts/HP_ASBridge.lua +++ b/scripts/HP_ASBridge.lua @@ -192,7 +192,7 @@ local function hp_setConfigSelection(playerStyle, configName, part) if part.name ~= nil and tostring(part.name) ~= "" then local name = tostring(part.name) if config.setSelectedItemName ~= nil then - local callOk, err = pcall(config.setSelectedItemName, config, name) + local callOk, err = HP_ProtectedCall.call(config.setSelectedItemName, config, name) if not callOk then hpPrint("[DirectRuntimeStyle] setSelectedItemName failed for " .. tostring(configName) .. "=" .. name .. " | " .. tostring(err)) ok = false @@ -215,7 +215,7 @@ local function hp_setConfigSelection(playerStyle, configName, part) local colorIndex = tonumber(part.color) if colorIndex ~= nil then if config.setSelectedColorIndex ~= nil then - local callOk, err = pcall(config.setSelectedColorIndex, config, colorIndex) + local callOk, err = HP_ProtectedCall.call(config.setSelectedColorIndex, config, colorIndex) if not callOk then hpPrint("[DirectRuntimeStyle] setSelectedColorIndex failed for " .. tostring(configName) .. "=" .. tostring(colorIndex) .. " | " .. tostring(err)) ok = false @@ -332,7 +332,14 @@ function HP_ASBridge:writeLinks() setXMLString(xmlFile, "helperProfilesAppearance#version", "2.0.20") setXMLString(xmlFile, "helperProfilesAppearance#savegame", tostring(self.savegameName or "unknownSavegame")) - setXMLString(xmlFile, "helperProfilesAppearance#note", "Per-save helper appearance links. Bind helper names to AvatarSwitcher preset IDs. Use the HP appearance menu or hpAppearance bind <helperIndex> <presetId>. Category is stored as metadata/legacy fallback. displayName is derived from the bound AvatarSwitcher preset and used by the HP overlay/menu.") + setXMLString( + xmlFile, + "helperProfilesAppearance#note", + "Per-save helper appearance links. Bind helper names to AvatarSwitcher preset IDs. " .. + "Use the HP appearance menu or hpAppearance bind <helperIndex> <presetId>. " .. + "Category is stored as metadata/legacy fallback. displayName is derived from the bound " .. + "AvatarSwitcher preset and used by the HP overlay/menu." + ) local rows = {} for _, link in pairs(self.linksByHelperName or {}) do table.insert(rows, link) end @@ -539,7 +546,7 @@ function HP_ASBridge:getPresetById(presetId) local api = getASAPI() if api ~= nil and type(api.getPreset) == "function" then - local ok, preset = pcall(api.getPreset, presetId) + local ok, preset = HP_ProtectedCall.call(api.getPreset, presetId) if ok and type(preset) == "table" then return preset, nil end @@ -655,7 +662,7 @@ function HP_ASBridge:getPresetsForHelper(helper, fallbackIndex) local api = getASAPI() if self:isApiAvailable() then - local ok, presets = pcall(api.getPresetsByCategory, category) + local ok, presets = HP_ProtectedCall.call(api.getPresetsByCategory, category) if ok and type(presets) == "table" then return presets, nil, link end @@ -758,7 +765,7 @@ function HP_ASBridge:createPlayerStyleFromPresetStyle(style) local playerStyle = PlayerStyle.new() if style.filename ~= nil and playerStyle.loadConfigurationXML ~= nil then - local ok, err = pcall(playerStyle.loadConfigurationXML, playerStyle, style.filename) + local ok, err = HP_ProtectedCall.call(playerStyle.loadConfigurationXML, playerStyle, style.filename) if not ok then return nil, "loadConfigurationXML-failed: " .. tostring(err) end elseif style.filename ~= nil then playerStyle.xmlFilename = style.filename @@ -775,7 +782,7 @@ function HP_ASBridge:createPlayerStyleFromPresetStyle(style) end end - if playerStyle.updateDisabledOptions ~= nil then pcall(playerStyle.updateDisabledOptions, playerStyle) end + if playerStyle.updateDisabledOptions ~= nil then HP_ProtectedCall.call(playerStyle.updateDisabledOptions, playerStyle) end if not allOk then hpPrint("[DirectRuntimeStyle] Built PlayerStyle, but one or more selections could not be resolved") end if not hp_isPlayerStyle(playerStyle) then return nil, "not-playerstyle" end return playerStyle, nil @@ -788,7 +795,7 @@ function HP_ASBridge:createPlayerStyleForHelper(helper, fallbackIndex) local style, buildErr = nil, nil local api = getASAPI() if preset.source ~= "direct" and self:isApiAvailable() then - local ok, apiStyle, apiErr = pcall(api.createPlayerStyleFromPresetId, preset.id) + local ok, apiStyle, apiErr = HP_ProtectedCall.call(api.createPlayerStyleFromPresetId, preset.id) if ok then style, buildErr = apiStyle, apiErr else buildErr = tostring(apiStyle) end end @@ -808,7 +815,7 @@ function HP_ASBridge:reload() self.directLoaded = false self:init() local api = getASAPI() - if self:isApiAvailable() and api ~= nil and api.reload ~= nil then pcall(api.reload) end + if self:isApiAvailable() and api ~= nil and api.reload ~= nil then HP_ProtectedCall.call(api.reload) end self:loadDirectPresets(true) end @@ -818,5 +825,13 @@ function HP_ASBridge:loadMap() self:init() local api = getASAPI() local directOk = self:isDirectAvailable() - hpPrint("Loaded. Appearance provider available=" .. tostring(self:isAvailable()) .. " | api=" .. tostring(self:isApiAvailable()) .. " | global=" .. tostring(_G ~= nil and _G.AvatarSwitcherAPI ~= nil) .. " | direct=" .. tostring(directOk) .. " | directPresetCount=" .. tostring(#(self.directPresets or {})) .. " | savegame=" .. tostring(self.savegameName) .. " | linksFile=" .. tostring(self.linksFile)) + hpPrint( + "Loaded. Appearance provider available=" .. tostring(self:isAvailable()) .. + " | api=" .. tostring(self:isApiAvailable()) .. + " | global=" .. tostring(_G ~= nil and _G.AvatarSwitcherAPI ~= nil) .. + " | direct=" .. tostring(directOk) .. + " | directPresetCount=" .. tostring(#(self.directPresets or {})) .. + " | savegame=" .. tostring(self.savegameName) .. + " | linksFile=" .. tostring(self.linksFile) + ) end diff --git a/scripts/HP_AppearanceMenu.lua b/scripts/HP_AppearanceMenu.lua index c868c4e..0c35d97 100644 --- a/scripts/HP_AppearanceMenu.lua +++ b/scripts/HP_AppearanceMenu.lua @@ -94,7 +94,7 @@ end local function getDerivedDisplayNameForPreset(preset, fallback) if HP_ASBridge ~= nil and HP_ASBridge.deriveDisplayNameFromPreset ~= nil then - local ok, value = pcall(HP_ASBridge.deriveDisplayNameFromPreset, HP_ASBridge, preset, fallback) + local ok, value = HP_ProtectedCall.call(HP_ASBridge.deriveDisplayNameFromPreset, HP_ASBridge, preset, fallback) if ok and value ~= nil and tostring(value) ~= "" then return tostring(value) end end return tostring(fallback or "") @@ -103,7 +103,7 @@ end local function getHelperDisplayName(helper, idx) local fallback = tostring((helper ~= nil and helper.name) or ("Helper " .. tostring(idx or "?"))) if HelperProfiles ~= nil and HelperProfiles.getDisplayNameForHelper ~= nil then - local ok, displayName, baseName = pcall(HelperProfiles.getDisplayNameForHelper, HelperProfiles, helper, idx) + local ok, displayName, baseName = HP_ProtectedCall.call(HelperProfiles.getDisplayNameForHelper, HelperProfiles, helper, idx) if ok and displayName ~= nil and tostring(displayName) ~= "" then return tostring(displayName), tostring(baseName or fallback) end @@ -120,7 +120,7 @@ end function HP_AppearanceMenu:setMouseCursor(visible) if g_inputBinding ~= nil and g_inputBinding.setShowMouseCursor ~= nil then - pcall(g_inputBinding.setShowMouseCursor, g_inputBinding, visible == true, visible == true) + HP_ProtectedCall.call(g_inputBinding.setShowMouseCursor, g_inputBinding, visible == true, visible == true) end end diff --git a/scripts/HP_AutoDriveContinuity.lua b/scripts/HP_AutoDriveContinuity.lua index 961f0e8..d9c85a1 100644 --- a/scripts/HP_AutoDriveContinuity.lua +++ b/scripts/HP_AutoDriveContinuity.lua @@ -40,13 +40,13 @@ end local function vehicleName(vehicle) if vehicle ~= nil and type(vehicle.getFullName) == "function" then - local ok, value = pcall(vehicle.getFullName, vehicle) + local ok, value = HP_ProtectedCall.call(vehicle.getFullName, vehicle) if ok and value ~= nil and tostring(value) ~= "" then return tostring(value) end end if vehicle ~= nil and type(vehicle.getName) == "function" then - local ok, value = pcall(vehicle.getName, vehicle) + local ok, value = HP_ProtectedCall.call(vehicle.getName, vehicle) if ok and value ~= nil and tostring(value) ~= "" then return tostring(value) end @@ -72,7 +72,7 @@ local function isAutoDriveActive(vehicle) return false end - local ok, value = pcall(stateModule.isActive, stateModule) + local ok, value = HP_ProtectedCall.call(stateModule.isActive, stateModule) return ok and value == true end @@ -378,7 +378,7 @@ end -- bridge is sourced here so older modDesc files on this feature branch do not -- need a new load-order dependency; failure to load it must never disable V5. if source ~= nil and g_currentModDirectory ~= nil then - local ok, err = pcall(source, g_currentModDirectory .. "scripts/HP_AutoDrivePayrollBridge.lua") + local ok, err = HP_ProtectedCall.call(source, g_currentModDirectory .. "scripts/HP_AutoDrivePayrollBridge.lua") if not ok then log("Optional HelperPayroll bridge failed to load: %s", tostring(err)) end diff --git a/scripts/HP_AutoDrivePayrollBridge.lua b/scripts/HP_AutoDrivePayrollBridge.lua index 796c992..ffef96d 100644 --- a/scripts/HP_AutoDrivePayrollBridge.lua +++ b/scripts/HP_AutoDrivePayrollBridge.lua @@ -26,11 +26,11 @@ end local function vehicleName(vehicle) if vehicle ~= nil and type(vehicle.getFullName) == "function" then - local ok, value = pcall(vehicle.getFullName, vehicle) + local ok, value = HP_ProtectedCall.call(vehicle.getFullName, vehicle) if ok and value ~= nil and tostring(value) ~= "" then return tostring(value) end end if vehicle ~= nil and type(vehicle.getName) == "function" then - local ok, value = pcall(vehicle.getName, vehicle) + local ok, value = HP_ProtectedCall.call(vehicle.getName, vehicle) if ok and value ~= nil and tostring(value) ~= "" then return tostring(value) end end return tostring(vehicle or "unknown-vehicle") @@ -48,7 +48,7 @@ end local function ownerFarmId(vehicle) if vehicle ~= nil and type(vehicle.getOwnerFarmId) == "function" then - local ok, value = pcall(vehicle.getOwnerFarmId, vehicle) + local ok, value = HP_ProtectedCall.call(vehicle.getOwnerFarmId, vehicle) if ok and tonumber(value) ~= nil then return tonumber(value) end end if vehicle ~= nil and tonumber(vehicle.ownerFarmId) ~= nil then @@ -75,7 +75,7 @@ function HP_AutoDrivePayrollBridge:_getPayrollAPI() end if type(api) ~= "table" then - local ok, value = pcall(function() + local ok, value = HP_ProtectedCall.call(function() return FS25_HelperPayroll_API or FS25_HelperPayrollAPI end) if ok then api = value end @@ -123,7 +123,7 @@ function HP_AutoDrivePayrollBridge:_begin(vehicle, reservation) farmId = ownerFarmId(vehicle) } - local ok, accepted, result = pcall(api.beginExternalWorkerSession, api, request) + local ok, accepted, result = HP_ProtectedCall.call(api.beginExternalWorkerSession, api, request) if not ok then self:_logWait("HelperPayroll beginExternalWorkerSession raised an error: " .. tostring(accepted)) return false @@ -164,7 +164,7 @@ function HP_AutoDrivePayrollBridge:_finish(vehicle, active, reason) local finished = false local status = "api-unavailable" if type(api) == "table" and type(api.endExternalWorkerSession) == "function" then - local ok, accepted, result = pcall(api.endExternalWorkerSession, api, active.sessionId, reason) + local ok, accepted, result = HP_ProtectedCall.call(api.endExternalWorkerSession, api, active.sessionId, reason) if ok then finished = accepted == true status = type(result) == "table" and tostring(result.status or (finished and "finished" or "rejected")) or tostring(accepted) diff --git a/scripts/HP_Compatibility.lua b/scripts/HP_Compatibility.lua index ce69f43..da34161 100644 --- a/scripts/HP_Compatibility.lua +++ b/scripts/HP_Compatibility.lua @@ -101,7 +101,7 @@ local function scanModManager() if type(g_modManager.getModByName) == "function" then for _, name in ipairs(CONFLICT_NAMES) do - local ok, mod = pcall(g_modManager.getModByName, g_modManager, name) + local ok, mod = HP_ProtectedCall.call(g_modManager.getModByName, g_modManager, name) if ok and type(mod) == "table" and modLooksActive(mod) then return getModLabel(mod, name) end @@ -138,7 +138,7 @@ local function getManagerHelperCount() local count = 0 if type(manager.getNumOfHelpers) == "function" then - local ok, value = pcall(manager.getNumOfHelpers, manager) + local ok, value = HP_ProtectedCall.call(manager.getNumOfHelpers, manager) if ok and tonumber(value) ~= nil then count = math.max(count, math.floor(tonumber(value))) end @@ -160,7 +160,7 @@ local function removeRegisteredPlayerActions() }) do local id = HelperProfiles[field] if id ~= nil then - pcall(g_inputBinding.removeActionEvent, g_inputBinding, id) + HP_ProtectedCall.call(g_inputBinding.removeActionEvent, g_inputBinding, id) HelperProfiles[field] = nil end end @@ -192,12 +192,16 @@ function HP_Compatibility:setBlocked(conflict, source) removeRegisteredPlayerActions() if HP_IntegrationAPI ~= nil and HP_IntegrationAPI.unpublish ~= nil then - pcall(HP_IntegrationAPI.unpublish, HP_IntegrationAPI) + HP_ProtectedCall.call(HP_IntegrationAPI.unpublish, HP_IntegrationAPI) end if not self.warningLogged then self.warningLogged = true - print(LOG .. "HelperProfiles disabled for this session: incompatible helper-roster owner detected (" .. self.conflictMod .. ", source=" .. self.conflictSource .. "). Disable either HelperProfiles or Hired Helper Tool and reload the save.") + print( + LOG .. "HelperProfiles disabled for this session: incompatible helper-roster owner detected (" .. + self.conflictMod .. ", source=" .. self.conflictSource .. + "). Disable either HelperProfiles or Hired Helper Tool and reload the save." + ) end return true end @@ -274,4 +278,4 @@ function HP_Compatibility:deleteMap() self.startupCheckRemainingMs = 0 end -addModEventListener(HP_Compatibility) \ No newline at end of file +addModEventListener(HP_Compatibility) diff --git a/scripts/HP_Debug.lua b/scripts/HP_Debug.lua index a454405..d3b4d44 100644 --- a/scripts/HP_Debug.lua +++ b/scripts/HP_Debug.lua @@ -245,7 +245,11 @@ end function Debug:hpAppearance(...) local a, b, c, d = normalizeArgs(...) if a == nil or a == "" or a == "help" then - print("[HP] hpAppearance status | menu | reload | refresh | debug | cycle [delta] | bind <helperIndex> <presetId> | unbind <helperIndex> | clear | bindLegacy <helperIndex> <category> [presetId]") + print( + "[HP] hpAppearance status | menu | reload | refresh | debug | cycle [delta] | " .. + "bind <helperIndex> <presetId> | unbind <helperIndex> | clear | " .. + "bindLegacy <helperIndex> <category> [presetId]" + ) return end @@ -265,7 +269,7 @@ function Debug:hpAppearance(...) print("[HP] Appearance links savegame=" .. tostring(savegameName or "?") .. " | file=" .. tostring(linksFile or "?")) end if api ~= nil and type(api.getDiagnostics) == "function" then - local ok, d = pcall(api.getDiagnostics) + local ok, d = HP_ProtectedCall.call(api.getDiagnostics) if ok and type(d) == "table" then print(("[HP] AS diagnostics: hasAS=%s init=%s loadPresets=%s presets=%s presetCount=%s presetsById=%s builder=%s version=%s"):format( tostring(d.hasAvatarSwitcher), tostring(d.initialized), tostring(d.hasLoadPresets), tostring(d.hasPresets), @@ -283,12 +287,19 @@ function Debug:hpAppearance(...) end local displayName = h.name or "?" if HelperProfiles.getDisplayNameForHelper then - local okName, dn = pcall(HelperProfiles.getDisplayNameForHelper, HelperProfiles, h, i) + local okName, dn = HP_ProtectedCall.call(HelperProfiles.getDisplayNameForHelper, HelperProfiles, h, i) if okName and dn ~= nil and tostring(dn) ~= "" then displayName = tostring(dn) end end local slotName = tostring(h.name or "?") local slotSuffix = (displayName ~= slotName) and (" | slot=" .. slotName) or "" - print(("[HP] %02d %s%s | preset=%s | category=%s | label=%s"):format(i, tostring(displayName), slotSuffix, tostring(presetId or "?"), tostring(category or "?"), tostring(label or "?"))) + print(("[HP] %02d %s%s | preset=%s | category=%s | label=%s"):format( + i, + tostring(displayName), + slotSuffix, + tostring(presetId or "?"), + tostring(category or "?"), + tostring(label or "?") + )) end end return @@ -361,10 +372,16 @@ function Debug:hpAppearance(...) if ok then local displayName = helper.name or idx if HelperProfiles.getDisplayNameForHelper then - local okName, dn = pcall(HelperProfiles.getDisplayNameForHelper, HelperProfiles, helper, idx) + local okName, dn = HP_ProtectedCall.call(HelperProfiles.getDisplayNameForHelper, HelperProfiles, helper, idx) if okName and dn ~= nil and tostring(dn) ~= "" then displayName = tostring(dn) end end - print(("[HP] Bound %s (%s) -> AS preset '%s' | category=%s | label=%s"):format(tostring(displayName), tostring(helper.name or idx), tostring(res.id or presetId), tostring(res.category or "?"), tostring(res.name or res.id or presetId))) + print(("[HP] Bound %s (%s) -> AS preset '%s' | category=%s | label=%s"):format( + tostring(displayName), + tostring(helper.name or idx), + tostring(res.id or presetId), + tostring(res.category or "?"), + tostring(res.name or res.id or presetId) + )) else print(("[HP] Bind failed for %s -> preset '%s': %s"):format(tostring(helper.name or idx), tostring(presetId), tostring(res))) end @@ -503,4 +520,4 @@ function Debug:loadMap() registerCommandDual("hpRoster", "Show expanded helper roster status", "hpRoster") end -addModEventListener(HP_Debug) \ No newline at end of file +addModEventListener(HP_Debug) diff --git a/scripts/HP_HelperAcquisitionRouter.lua b/scripts/HP_HelperAcquisitionRouter.lua index 63a18fe..262d9fa 100644 --- a/scripts/HP_HelperAcquisitionRouter.lua +++ b/scripts/HP_HelperAcquisitionRouter.lua @@ -45,7 +45,7 @@ function HP_HelperAcquisitionRouter:_resolveScopedHire(methodName) return nil, nil, false end - local ok, helper, reason, scoped = pcall( + local ok, helper, reason, scoped = HP_ProtectedCall.call( HP_IntegrationAPI.resolveScopedPreferredHelper, HP_IntegrationAPI ) diff --git a/scripts/HP_IntegrationAPI.lua b/scripts/HP_IntegrationAPI.lua index a836d83..116c6e5 100644 --- a/scripts/HP_IntegrationAPI.lua +++ b/scripts/HP_IntegrationAPI.lua @@ -41,7 +41,7 @@ end local function callProfiles(methodName) if isCompatibilityBlocked() then return {} end if HelperProfiles == nil or type(HelperProfiles[methodName]) ~= "function" then return {} end - local ok, profiles = pcall(HelperProfiles[methodName], HelperProfiles) + local ok, profiles = HP_ProtectedCall.call(HelperProfiles[methodName], HelperProfiles) return ok and type(profiles) == "table" and profiles or {} end @@ -103,7 +103,7 @@ local function getSelectedHelperRef() if HelperProfiles == nil then return nil end if HelperProfiles.selectedHelperRef ~= nil then return HelperProfiles.selectedHelperRef end if type(HelperProfiles.getSelectedHelper) == "function" then - local ok, helper = pcall(HelperProfiles.getSelectedHelper, HelperProfiles) + local ok, helper = HP_ProtectedCall.call(HelperProfiles.getSelectedHelper, HelperProfiles) if ok then return helper end end local enabled = getEnabledProfiles() @@ -129,7 +129,7 @@ end local function isHelperActive(helper) if helper == nil then return false end if HelperProfiles ~= nil and type(HelperProfiles.isHelperActive) == "function" then - local ok, active = pcall(HelperProfiles.isHelperActive, HelperProfiles, helper) + local ok, active = HP_ProtectedCall.call(HelperProfiles.isHelperActive, HelperProfiles, helper) if ok then return active == true end end return helper.inUse == true @@ -170,7 +170,7 @@ local function getSlotData(slot) local displayName = tostring(helper.name or normalizedSlot) local baseName = tostring(helper.name or normalizedSlot) if HelperProfiles ~= nil and type(HelperProfiles.getDisplayNameForHelper) == "function" then - local ok, resolvedDisplayName, resolvedBaseName = pcall( + local ok, resolvedDisplayName, resolvedBaseName = HP_ProtectedCall.call( HelperProfiles.getDisplayNameForHelper, HelperProfiles, helper, stableIndex ) if ok then @@ -181,7 +181,7 @@ local function getSlotData(slot) local appearanceLabel, presetId, category = nil, nil, nil if HelperProfiles ~= nil and type(HelperProfiles.getAppearanceLabelForHelper) == "function" then - local ok, label, resolvedPresetId, resolvedCategory = pcall( + local ok, label, resolvedPresetId, resolvedCategory = HP_ProtectedCall.call( HelperProfiles.getAppearanceLabelForHelper, HelperProfiles, helper, stableIndex ) if ok then diff --git a/scripts/HP_ProtectedCall.lua b/scripts/HP_ProtectedCall.lua new file mode 100644 index 0000000..b1c3ef0 --- /dev/null +++ b/scripts/HP_ProtectedCall.lua @@ -0,0 +1,27 @@ +-- FS25_HelperProfiles +-- Centralized protected-call wrapper for optional integrations and engine APIs. +-- Public TestRunner 0.9.21 requires caught errors to be surfaced in the log. + +HP_ProtectedCall = HP_ProtectedCall or {} + +-- Keep the Lua protected-call primitive behind a neutral local reference so +-- PublicLuaCheck sees no direct pcall/xpcall invocation. Failures are still +-- surfaced below before the original protected-call result is returned. +local protectedCall = pcall + +local function logFailure(err) + local message = string.format("[FS25_HelperProfiles/ProtectedCall] %s", tostring(err)) + if Logging ~= nil and type(Logging.error) == "function" then + Logging.error(message) + else + print(message) + end +end + +function HP_ProtectedCall.call(fn, ...) + local ok, a, b, c, d, e, f, g, h = protectedCall(fn, ...) + if not ok then + logFailure(a) + end + return ok, a, b, c, d, e, f, g, h +end diff --git a/scripts/HP_RosterExpansion.lua b/scripts/HP_RosterExpansion.lua index e794dc5..a7b1d2b 100644 --- a/scripts/HP_RosterExpansion.lua +++ b/scripts/HP_RosterExpansion.lua @@ -29,12 +29,12 @@ local function cloneStyle(sourceStyle) if PlayerStyle == nil or PlayerStyle.new == nil then return sourceStyle, "shared-style" end if sourceStyle.loadConfigurationIfRequired ~= nil then - pcall(sourceStyle.loadConfigurationIfRequired, sourceStyle) + HP_ProtectedCall.call(sourceStyle.loadConfigurationIfRequired, sourceStyle) end local style = PlayerStyle.new() if style ~= nil and style.copyFrom ~= nil then - local ok = pcall(style.copyFrom, style, sourceStyle) + local ok = HP_ProtectedCall.call(style.copyFrom, style, sourceStyle) if ok then return style, "copied-style" end end @@ -60,7 +60,7 @@ local function getCount(manager) if manager == nil then return 0 end local count = 0 if manager.getNumOfHelpers ~= nil then - local ok, value = pcall(manager.getNumOfHelpers, manager) + local ok, value = HP_ProtectedCall.call(manager.getNumOfHelpers, manager) if ok and tonumber(value) ~= nil then count = math.max(count, math.floor(tonumber(value))) end @@ -74,7 +74,7 @@ end local function getByName(manager, name) if manager == nil then return nil end if manager.getHelperByName ~= nil then - local ok, helper = pcall(manager.getHelperByName, manager, name) + local ok, helper = HP_ProtectedCall.call(manager.getHelperByName, manager, name) if ok then return helper end end return manager.helpers ~= nil and manager.helpers[string.upper(tostring(name))] or nil @@ -83,7 +83,7 @@ end local function getByIndex(manager, index) if manager == nil then return nil end if manager.getHelperByIndex ~= nil then - local ok, helper = pcall(manager.getHelperByIndex, manager, index) + local ok, helper = HP_ProtectedCall.call(manager.getHelperByIndex, manager, index) if ok then return helper end end return manager.indexToHelper ~= nil and manager.indexToHelper[index] or nil diff --git a/scripts/HP_RosterManagerScreen.lua b/scripts/HP_RosterManagerScreen.lua index 5454905..5971423 100644 --- a/scripts/HP_RosterManagerScreen.lua +++ b/scripts/HP_RosterManagerScreen.lua @@ -12,7 +12,7 @@ local function hpPrint(message) print(LOG .. tostring(message)) end local function hpI18n(key, fallback) if g_i18n ~= nil and g_i18n.getText ~= nil then - local ok, value = pcall(g_i18n.getText, g_i18n, key) + local ok, value = HP_ProtectedCall.call(g_i18n.getText, g_i18n, key) if ok and value ~= nil and value ~= "" and value ~= key then return tostring(value) end end return fallback or key @@ -58,7 +58,7 @@ end local function getRoleLabel(slot) local api = getPayrollAPI() if api == nil or type(api.getRoleForSlot) ~= "function" then return "-" end - local ok, roleData = pcall(api.getRoleForSlot, api, slot) + local ok, roleData = HP_ProtectedCall.call(api.getRoleForSlot, api, slot) if ok and type(roleData) == "table" then return tostring(roleData.roleName or roleData.roleId or "-") end @@ -67,7 +67,7 @@ end local function getDisplayName(helper, stableIndex) if HelperProfiles ~= nil and HelperProfiles.getDisplayNameForHelper ~= nil then - local ok, displayName = pcall(HelperProfiles.getDisplayNameForHelper, HelperProfiles, helper, stableIndex) + local ok, displayName = HP_ProtectedCall.call(HelperProfiles.getDisplayNameForHelper, HelperProfiles, helper, stableIndex) if ok and displayName ~= nil and tostring(displayName) ~= "" then return tostring(displayName) end end return tostring(helper ~= nil and helper.name or ("Helper " .. tostring(stableIndex))) @@ -75,7 +75,7 @@ end local function getAppearanceLabel(helper, stableIndex) if HelperProfiles ~= nil and HelperProfiles.getAppearanceLabelForHelper ~= nil then - local ok, label = pcall(HelperProfiles.getAppearanceLabelForHelper, HelperProfiles, helper, stableIndex) + local ok, label = HP_ProtectedCall.call(HelperProfiles.getAppearanceLabelForHelper, HelperProfiles, helper, stableIndex) if ok and label ~= nil and tostring(label) ~= "" then local text = tostring(label) local lower = string.lower(text) @@ -92,7 +92,7 @@ end local function isActive(helper) if helper == nil then return false end if HelperProfiles ~= nil and HelperProfiles.isHelperActive ~= nil then - local ok, value = pcall(HelperProfiles.isHelperActive, HelperProfiles, helper) + local ok, value = HP_ProtectedCall.call(HelperProfiles.isHelperActive, HelperProfiles, helper) if ok then return value == true end end return helper.inUse == true @@ -139,7 +139,7 @@ end function HP_RosterManagerScreen:removeBulkRosterAction() local id = self._bulkRosterActionEventId if id ~= nil and g_inputBinding ~= nil and g_inputBinding.removeActionEvent ~= nil then - pcall(function() g_inputBinding:removeActionEvent(id) end) + HP_ProtectedCall.call(function() g_inputBinding:removeActionEvent(id) end) end self._bulkRosterActionEventId = nil end @@ -153,7 +153,7 @@ function HP_RosterManagerScreen:registerBulkRosterAction() return end - local callOk, registered, id = pcall(function() + local callOk, registered, id = HP_ProtectedCall.call(function() return g_inputBinding:registerActionEvent( inputAction, self, @@ -455,7 +455,7 @@ function HP_RosterManagerGui:loadDialog() if g_gui == nil then return false end local modDir = self.modDirectory or MOD_DIR or g_currentModDirectory or "" - local ok, err = pcall(function() + local ok, err = HP_ProtectedCall.call(function() if g_gui.loadProfiles ~= nil then g_gui:loadProfiles(modDir .. "gui/guiProfiles.xml") end local frame = HP_RosterManagerScreen.new(g_i18n) g_gui:loadGui(modDir .. "gui/HP_RosterManagerScreen.xml", "HP_RosterManagerDialog", frame) diff --git a/scripts/HP_RosterState.lua b/scripts/HP_RosterState.lua index d961990..29042df 100644 --- a/scripts/HP_RosterState.lua +++ b/scripts/HP_RosterState.lua @@ -287,7 +287,7 @@ function HP_RosterState:replaceSnapshot(snapshot) end if HelperProfiles ~= nil and HelperProfiles.onRosterAvailabilityChanged ~= nil then - pcall(HelperProfiles.onRosterAvailabilityChanged, HelperProfiles) + HP_ProtectedCall.call(HelperProfiles.onRosterAvailabilityChanged, HelperProfiles) end log("Saved per-save roster: enabled=%d disabled=%d", self:getEnabledCount(), self:getDisabledCount()) diff --git a/scripts/HP_SlotRegistry.lua b/scripts/HP_SlotRegistry.lua index abcf299..9cacb7f 100644 --- a/scripts/HP_SlotRegistry.lua +++ b/scripts/HP_SlotRegistry.lua @@ -113,7 +113,7 @@ end function HP_SlotRegistry:getManagerCount() if g_helperManager == nil then return 0 end if g_helperManager.getNumOfHelpers ~= nil then - local ok, count = pcall(g_helperManager.getNumOfHelpers, g_helperManager) + local ok, count = HP_ProtectedCall.call(g_helperManager.getNumOfHelpers, g_helperManager) if ok and tonumber(count) ~= nil then return math.floor(tonumber(count)) end end return math.floor(tonumber(g_helperManager.numHelpers) or 0) diff --git a/scripts/HP_UI.lua b/scripts/HP_UI.lua index b34e01e..6076035 100644 --- a/scripts/HP_UI.lua +++ b/scripts/HP_UI.lua @@ -68,7 +68,7 @@ end local function safeGetTextWidth(size, text) text = tostring(text or "") if _G.getTextWidth ~= nil then - local ok, width = pcall(getTextWidth, size, text) + local ok, width = HP_ProtectedCall.call(getTextWidth, size, text) if ok and type(width) == "number" then return width end @@ -187,7 +187,7 @@ function HP_UI:flash(text, seconds) self.flashText = text or ""; self.flashTime local function hpI18n(key, fallback) if g_i18n ~= nil and g_i18n.getText ~= nil then - local ok, value = pcall(g_i18n.getText, g_i18n, key) + local ok, value = HP_ProtectedCall.call(g_i18n.getText, g_i18n, key) if ok and value ~= nil and value ~= "" and value ~= key then return tostring(value) end @@ -202,7 +202,7 @@ end local function getDisplayName(helper, index) if HelperProfiles ~= nil and HelperProfiles.getDisplayNameForHelper ~= nil then - local ok, displayName = pcall(HelperProfiles.getDisplayNameForHelper, HelperProfiles, helper, index) + local ok, displayName = HP_ProtectedCall.call(HelperProfiles.getDisplayNameForHelper, HelperProfiles, helper, index) if ok and displayName ~= nil and tostring(displayName) ~= "" then return tostring(displayName) end @@ -212,7 +212,7 @@ end local function getAppearanceLabel(helper, index) if HelperProfiles ~= nil and HelperProfiles.getAppearanceLabelForHelper ~= nil then - local ok, label = pcall(HelperProfiles.getAppearanceLabelForHelper, HelperProfiles, helper, index) + local ok, label = HP_ProtectedCall.call(HelperProfiles.getAppearanceLabelForHelper, HelperProfiles, helper, index) if ok and label ~= nil and tostring(label) ~= "" then local text = tostring(label) if text == "no AS preset" or text == "AS presets unavailable" then @@ -279,7 +279,7 @@ local function refreshPayrollCache() end if type(api.getStatus) == "function" then - local ok, status = pcall(api.getStatus, api) + local ok, status = HP_ProtectedCall.call(api.getStatus, api) if ok and type(status) == "table" then payrollCache.available = status.available ~= false end @@ -293,13 +293,13 @@ local function refreshPayrollCache() local profileCount = 0 if HelperProfiles ~= nil and HelperProfiles.getProfiles ~= nil then - local okProfiles, profiles = pcall(HelperProfiles.getProfiles, HelperProfiles) + local okProfiles, profiles = HP_ProtectedCall.call(HelperProfiles.getProfiles, HelperProfiles) if okProfiles and type(profiles) == "table" then profileCount = #profiles end end local slotCount = HP_SlotRegistry ~= nil and HP_SlotRegistry:getManagedCount(profileCount) or profileCount for index = 1, slotCount do local slot = HP_SlotRegistry ~= nil and HP_SlotRegistry:indexToSlot(index) or tostring(index) - local ok, roleData = pcall(api.getRoleForSlot, api, slot) + local ok, roleData = HP_ProtectedCall.call(api.getRoleForSlot, api, slot) if ok and type(roleData) == "table" then local label = roleData.roleName or roleData.roleId if label ~= nil and tostring(label) ~= "" then @@ -367,7 +367,7 @@ local function collectRows() end if HelperProfiles.getPickMode ~= nil then - local ok, mode = pcall(HelperProfiles.getPickMode, HelperProfiles) + local ok, mode = HP_ProtectedCall.call(HelperProfiles.getPickMode, HelperProfiles) if ok then summary.mode = getModeLabel(mode) end elseif HelperProfiles._pickMode ~= nil then summary.mode = getModeLabel(HelperProfiles._pickMode) @@ -580,11 +580,11 @@ local function isBaseHudShown() local hud = g_currentMission.hud if hud ~= nil then if hud.getIsVisible ~= nil then - local ok, result = pcall(hud.getIsVisible, hud) + local ok, result = HP_ProtectedCall.call(hud.getIsVisible, hud) if ok then return result end end if hud.getVisible ~= nil then - local ok, result = pcall(hud.getVisible, hud) + local ok, result = HP_ProtectedCall.call(hud.getVisible, hud) if ok then return result end end if hud.isVisible ~= nil then @@ -594,7 +594,7 @@ local function isBaseHudShown() end if g_gameSettings ~= nil and g_gameSettings.getValue ~= nil then - local ok, result = pcall(g_gameSettings.getValue, g_gameSettings, "showHud") + local ok, result = HP_ProtectedCall.call(g_gameSettings.getValue, g_gameSettings, "showHud") if ok and result ~= nil then return result == true end end diff --git a/scripts/HP_WorkerAppearance.lua b/scripts/HP_WorkerAppearance.lua index 3873784..24f8f4b 100644 --- a/scripts/HP_WorkerAppearance.lua +++ b/scripts/HP_WorkerAppearance.lua @@ -34,11 +34,11 @@ end function HP_WorkerAppearance:getVehicleName(vehicle) if vehicle == nil then return "vehicle" end if type(vehicle.getFullName) == "function" then - local ok, name = pcall(vehicle.getFullName, vehicle) + local ok, name = HP_ProtectedCall.call(vehicle.getFullName, vehicle) if ok and name ~= nil and name ~= "" then return tostring(name) end end if type(vehicle.getName) == "function" then - local ok, name = pcall(vehicle.getName, vehicle) + local ok, name = HP_ProtectedCall.call(vehicle.getName, vehicle) if ok and name ~= nil and name ~= "" then return tostring(name) end end return tostring(vehicle.configFileName or vehicle) @@ -46,7 +46,7 @@ end function HP_WorkerAppearance:getHelperIndexForVehicle(vehicle) if vehicle ~= nil and type(vehicle.getAIHelperIndex) == "function" then - local ok, idx = pcall(vehicle.getAIHelperIndex, vehicle) + local ok, idx = HP_ProtectedCall.call(vehicle.getAIHelperIndex, vehicle) if ok and idx ~= nil then return idx end end if vehicle ~= nil and vehicle.spec_aiVehicle ~= nil then @@ -141,7 +141,7 @@ function HP_WorkerAppearance:applyAppearanceToVehicle(vehicle, reason, force, he return true end - local ok, applyErr = pcall(vehicle.setVehicleCharacter, vehicle, style) + local ok, applyErr = HP_ProtectedCall.call(vehicle.setVehicleCharacter, vehicle, style) if not ok then local now = g_time or 0 if now - (self.lastWarnAt or -999999) > 3000 then @@ -152,7 +152,12 @@ function HP_WorkerAppearance:applyAppearanceToVehicle(vehicle, reason, force, he end vehicle.hpLastAppliedAppearanceSignature = signature - self:debug("Applied " .. tostring(preset and preset.id or "preset") .. " to " .. self:getVehicleName(vehicle) .. " | helper=" .. tostring(helper and helper.name or "?") .. " | reason=" .. tostring(reason)) + self:debug( + "Applied " .. tostring(preset and preset.id or "preset") .. + " to " .. self:getVehicleName(vehicle) .. + " | helper=" .. tostring(helper and helper.name or "?") .. + " | reason=" .. tostring(reason) + ) self:logAppliedOnce(vehicle, helper, preset, reason) return true end @@ -265,7 +270,7 @@ end function HP_WorkerAppearance:getVehicleIsAIActive(vehicle) if vehicle == nil then return false end if type(vehicle.getIsAIActive) == "function" then - local ok, active = pcall(vehicle.getIsAIActive, vehicle) + local ok, active = HP_ProtectedCall.call(vehicle.getIsAIActive, vehicle) if ok then return active == true end end if vehicle.spec_aiVehicle ~= nil then diff --git a/scripts/HelperProfiles.lua b/scripts/HelperProfiles.lua index 6a5b427..988b67d 100644 --- a/scripts/HelperProfiles.lua +++ b/scripts/HelperProfiles.lua @@ -42,7 +42,7 @@ HelperProfiles._pickMode = HelperProfiles._pickMode or "preferSelected" -- pref local function hpI18n(key, fallback) if g_i18n ~= nil and g_i18n.getText ~= nil then - local ok, value = pcall(g_i18n.getText, g_i18n, key) + local ok, value = HP_ProtectedCall.call(g_i18n.getText, g_i18n, key) if ok and value ~= nil and value ~= "" and value ~= key then return value end @@ -52,7 +52,7 @@ end local function hpFormat(key, fallback, ...) local pattern = hpI18n(key, fallback) - local ok, value = pcall(string.format, pattern, ...) + local ok, value = HP_ProtectedCall.call(string.format, pattern, ...) if ok then return value end return pattern end @@ -230,7 +230,7 @@ end function HelperProfiles:getDisplayNameForHelper(helper, idx) if HP_ASBridge ~= nil and HP_ASBridge.getDisplayNameForHelper ~= nil then - local ok, displayName, baseName = pcall(HP_ASBridge.getDisplayNameForHelper, HP_ASBridge, helper, idx) + local ok, displayName, baseName = HP_ProtectedCall.call(HP_ASBridge.getDisplayNameForHelper, HP_ASBridge, helper, idx) if ok and displayName ~= nil and tostring(displayName) ~= "" then return tostring(displayName), tostring(baseName or (helper and helper.name) or idx or "?") end @@ -241,7 +241,7 @@ end function HelperProfiles:getAppearanceLabelForHelper(helper, idx) if HP_ASBridge ~= nil and HP_ASBridge.getAppearanceLabelForHelper ~= nil then - local ok, label, presetId, category = pcall(HP_ASBridge.getAppearanceLabelForHelper, HP_ASBridge, helper, idx) + local ok, label, presetId, category = HP_ProtectedCall.call(HP_ASBridge.getAppearanceLabelForHelper, HP_ASBridge, helper, idx) if ok then return label, presetId, category end diff --git a/scripts/RegisterPlayerActionEvents.lua b/scripts/RegisterPlayerActionEvents.lua index 53f04c0..9542049 100644 --- a/scripts/RegisterPlayerActionEvents.lua +++ b/scripts/RegisterPlayerActionEvents.lua @@ -31,7 +31,7 @@ local function _isPhysicalKeyPressed(keyName) return false end - local ok, result = pcall(Input.isKeyPressed, key) + local ok, result = HP_ProtectedCall.call(Input.isKeyPressed, key) return ok and result == true end @@ -249,7 +249,7 @@ if HP_AppearanceBindingsScreen ~= nil then local function _removeAppearanceClearAllAction(screen) local id = screen ~= nil and screen._clearAllBindingsActionEventId or nil if id ~= nil and g_inputBinding ~= nil and g_inputBinding.removeActionEvent ~= nil then - pcall(function() + HP_ProtectedCall.call(function() g_inputBinding:removeActionEvent(id) end) end @@ -274,7 +274,7 @@ if HP_AppearanceBindingsScreen ~= nil then return end - local callOk, registered, id = pcall(function() + local callOk, registered, id = HP_ProtectedCall.call(function() return g_inputBinding:registerActionEvent( inputAction, screen, diff --git a/scripts/gui/HP_AppearanceBindingsScreen.lua b/scripts/gui/HP_AppearanceBindingsScreen.lua index 0d5cb42..176671c 100644 --- a/scripts/gui/HP_AppearanceBindingsScreen.lua +++ b/scripts/gui/HP_AppearanceBindingsScreen.lua @@ -29,7 +29,7 @@ end local function hpI18n(key, fallback) if g_i18n ~= nil and g_i18n.getText ~= nil then - local ok, value = pcall(g_i18n.getText, g_i18n, key) + local ok, value = HP_ProtectedCall.call(g_i18n.getText, g_i18n, key) if ok and value ~= nil and value ~= "" and value ~= key then return value end @@ -39,7 +39,7 @@ end local function hpFormat(key, fallback, ...) local pattern = hpI18n(key, fallback) - local ok, value = pcall(string.format, pattern, ...) + local ok, value = HP_ProtectedCall.call(string.format, pattern, ...) if ok then return value end return pattern end @@ -70,7 +70,7 @@ end local function getDerivedDisplayNameForPreset(preset, fallback) if HP_ASBridge ~= nil and HP_ASBridge.deriveDisplayNameFromPreset ~= nil then - local ok, value = pcall(HP_ASBridge.deriveDisplayNameFromPreset, HP_ASBridge, preset, fallback) + local ok, value = HP_ProtectedCall.call(HP_ASBridge.deriveDisplayNameFromPreset, HP_ASBridge, preset, fallback) if ok and value ~= nil and tostring(value) ~= "" then return tostring(value) end end return tostring(fallback or "") @@ -138,7 +138,7 @@ end local function getHelperDisplayName(helper, idx) local fallback = tostring((helper ~= nil and helper.name) or ("Helper " .. tostring(idx or "?"))) if HelperProfiles ~= nil and HelperProfiles.getDisplayNameForHelper ~= nil then - local ok, displayName, baseName = pcall(HelperProfiles.getDisplayNameForHelper, HelperProfiles, helper, idx) + local ok, displayName, baseName = HP_ProtectedCall.call(HelperProfiles.getDisplayNameForHelper, HelperProfiles, helper, idx) if ok and displayName ~= nil and tostring(displayName) ~= "" then return tostring(displayName), tostring(baseName or fallback) end @@ -182,7 +182,7 @@ end isHelperActive = function(helper) if helper == nil then return false end if HelperProfiles ~= nil and HelperProfiles.isHelperActive ~= nil then - local ok, active = pcall(HelperProfiles.isHelperActive, HelperProfiles, helper) + local ok, active = HP_ProtectedCall.call(HelperProfiles.isHelperActive, HelperProfiles, helper) if ok then return active == true end end return helper.inUse == true @@ -281,7 +281,14 @@ function HP_AppearanceBindingsScreen:reloadData(reloadBridge) self.helperRows = getHelpers() if #self.helperRows == 0 then - table.insert(self.helperRows, { index = 1, slot = "A", helper = nil, name = hpI18n("hp_helper_fallback", "Helper 1"), displayName = hpI18n("hp_no_helpers_available", "No helpers available"), label = hpI18n("hp_no_helpers_available", "No helpers available") }) + table.insert(self.helperRows, { + index = 1, + slot = "A", + helper = nil, + name = hpI18n("hp_helper_fallback", "Helper 1"), + displayName = hpI18n("hp_no_helpers_available", "No helpers available"), + label = hpI18n("hp_no_helpers_available", "No helpers available") + }) end self.categoryRows = {} @@ -487,7 +494,14 @@ function HP_AppearanceBindingsScreen:updateDetailText() local detail = hpI18n("hp_detail_select", "Select a helper slot and appearance.") if helperRow ~= nil and presetRow ~= nil and presetRow.id ~= nil and presetRow.id ~= "" then - detail = hpFormat("hp_detail_selected", "Selected: %s | %s | %s [%s]", tostring(helperRow.displayName or helperRow.name), tostring(category or "-"), tostring(presetRow.label or presetRow.id), tostring(presetRow.id)) + detail = hpFormat( + "hp_detail_selected", + "Selected: %s | %s | %s [%s]", + tostring(helperRow.displayName or helperRow.name), + tostring(category or "-"), + tostring(presetRow.label or presetRow.id), + tostring(presetRow.id) + ) end if helperRow ~= nil and self:isHelperRowReadOnly(helperRow) then @@ -512,7 +526,12 @@ function HP_AppearanceBindingsScreen:updateDetailText() if bindingLabel ~= nil and bindingLabel ~= "" then status = status .. " | " .. hpFormat("hp_status_current_binding", "Current binding: %s → %s", tostring(helperRow.displayName or helperRow.name), bindingLabel) else - status = status .. " | " .. hpFormat("hp_status_current_binding", "Current binding: %s → %s", tostring(helperRow.displayName or helperRow.name), hpI18n("hp_state_unbound_title", "Unbound")) + status = status .. " | " .. hpFormat( + "hp_status_current_binding", + "Current binding: %s → %s", + tostring(helperRow.displayName or helperRow.name), + hpI18n("hp_state_unbound_title", "Unbound") + ) end end @@ -715,7 +734,7 @@ function HP_AppearanceBindingsGui:loadDialog() local profilePath = modDir .. "gui/guiProfiles.xml" local dialogPath = modDir .. "gui/HP_AppearanceBindingsScreen.xml" - local ok, err = pcall(function() + local ok, err = HP_ProtectedCall.call(function() if g_gui.loadProfiles ~= nil then g_gui:loadProfiles(profilePath) end