Fix workshop search appID lookup and checkbox picker
Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
parent
1514063525
commit
d8ad1aa924
7 changed files with 57 additions and 16 deletions
|
|
@ -1,6 +1,8 @@
|
|||
# Changelog
|
||||
|
||||
## 2026-01-31
|
||||
- Added adapter AppID lookup for Workshop search so the picker can query Steam even when the server XML lacks a clear default installer entry.
|
||||
- Switched Workshop picker results to checkbox selection, letting customers toggle multiple mods directly from the search list.
|
||||
- Added a hardened Workshop scraping helper (the same HTML workflow we validated manually) and wired it into the Steam Workshop service as a fallback whenever the official API errors out or returns empty data.
|
||||
- Added a native PHP HTTP scraper fallback (auto-selected when bash/proc_open is unavailable, e.g., on Windows XAMPP installs) so the Game Monitor search stops showing “Unable to contact the Steam Workshop” when the API dies but the HTML workflow still works.
|
||||
- Surface scraper vs API attempts (including shell commands, exit codes, and stderr) in the JSON response so the Game Monitor can show exactly which backend produced the results.
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@
|
|||
- Allow players/admins to reorder selected Workshop mods in the new picker UI so load order matches game expectations.
|
||||
- Surface pagination controls in the Workshop picker so users can request additional batches from the new Steam Web API search endpoint.
|
||||
- Add an admin-facing toggle that makes it clear when the HTML scraper fallback is in use and lets staff force API-only mode if Valve ever objects.
|
||||
- Add Workshop result preview thumbnails and author links in the picker for easier browsing.
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Last Updated at 11:40am on 2025-06-12
|
||||
Last Updated at 6:32pm on 2026-01-31
|
||||
|
|
|
|||
|
|
@ -620,6 +620,20 @@ class SteamWorkshopService
|
|||
|
||||
public function getSteamAppIdForGameKey(string $gameKey): ?string
|
||||
{
|
||||
$gameKey = trim($gameKey);
|
||||
if ($gameKey === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$adapterKey = $this->getAdapterKeyForGame($gameKey);
|
||||
if ($adapterKey !== null && $adapterKey !== '') {
|
||||
$adapter = $this->getAdapterByKey($adapterKey);
|
||||
$adapterAppId = isset($adapter['steam_app_id']) ? trim((string)$adapter['steam_app_id']) : '';
|
||||
if ($adapterAppId !== '') {
|
||||
return $adapterAppId;
|
||||
}
|
||||
}
|
||||
|
||||
$xml = $this->loadServerConfigXml($gameKey);
|
||||
if ($xml === null) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -514,6 +514,12 @@
|
|||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.sw-picker__results-hint {
|
||||
margin: 0.35rem 0 0.6rem;
|
||||
color: #555;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.sw-picker__request-row {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
|
@ -563,6 +569,17 @@
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.sw-picker__result-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.sw-picker__result-toggle input {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.sw-picker__empty {
|
||||
color: #777;
|
||||
font-size: 0.9rem;
|
||||
|
|
|
|||
|
|
@ -109,17 +109,22 @@
|
|||
});
|
||||
}
|
||||
if (this.resultsBody) {
|
||||
this.resultsBody.addEventListener('click', function (event) {
|
||||
this.resultsBody.addEventListener('change', function (event) {
|
||||
var target = event.target;
|
||||
if (!(target instanceof HTMLElement)) {
|
||||
if (!(target instanceof HTMLInputElement)) {
|
||||
return;
|
||||
}
|
||||
if (target.matches('.js-sw-add')) {
|
||||
if (target.matches('.js-sw-result-toggle')) {
|
||||
var payload = target.getAttribute('data-payload');
|
||||
if (payload) {
|
||||
try {
|
||||
var data = JSON.parse(payload);
|
||||
_this.addSelected(data);
|
||||
if (target.checked) {
|
||||
_this.addSelected(data);
|
||||
}
|
||||
else {
|
||||
_this.removeSelected(String(data.id));
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('Invalid payload', err);
|
||||
|
|
@ -208,15 +213,18 @@
|
|||
};
|
||||
var row = document.createElement('tr');
|
||||
var selectCell = document.createElement('td');
|
||||
var action = document.createElement('button');
|
||||
action.type = 'button';
|
||||
action.className = 'btn btn-xs sw-picker__action js-sw-add';
|
||||
action.textContent = this_1.lang.add;
|
||||
action.setAttribute('data-payload', JSON.stringify(normalized));
|
||||
if (this_1.isSelected(normalized.id)) {
|
||||
action.disabled = true;
|
||||
}
|
||||
selectCell.appendChild(action);
|
||||
var toggle = document.createElement('label');
|
||||
toggle.className = 'sw-picker__result-toggle';
|
||||
var checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.className = 'js-sw-result-toggle';
|
||||
checkbox.setAttribute('data-payload', JSON.stringify(normalized));
|
||||
checkbox.checked = this_1.isSelected(normalized.id);
|
||||
toggle.appendChild(checkbox);
|
||||
var toggleText = document.createElement('span');
|
||||
toggleText.textContent = this_1.lang.add;
|
||||
toggle.appendChild(toggleText);
|
||||
selectCell.appendChild(toggle);
|
||||
var titleCell = document.createElement('td');
|
||||
titleCell.innerHTML = '<strong>' + this_1.escape(normalized.label) + '</strong><div class="sw-picker__result-meta">#' + this_1.escape(normalized.id) + '</div>';
|
||||
var authorCell = document.createElement('td');
|
||||
|
|
@ -261,14 +269,12 @@
|
|||
});
|
||||
this.persist();
|
||||
this.renderSelected();
|
||||
this.renderResults(this.lastResults || []);
|
||||
};
|
||||
Picker.prototype.removeSelected = function (id) {
|
||||
var next = this.state.selected.filter(function (item) { return item.id !== id; });
|
||||
this.state.selected = next;
|
||||
this.persist();
|
||||
this.renderSelected();
|
||||
this.renderResults(this.lastResults || []);
|
||||
};
|
||||
Picker.prototype.toggleSelected = function (id, enabled) {
|
||||
var changed = false;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ $langAttrs = [
|
|||
|
||||
<div class="sw-picker__results">
|
||||
<h5><?php echo htmlspecialchars($lang['mod_picker_results_heading'] ?? 'Search results'); ?></h5>
|
||||
<p class="sw-picker__results-hint"><?php echo htmlspecialchars($lang['mod_picker_results_hint'] ?? 'Check the mods you want to add.'); ?></p>
|
||||
<div class="sw-picker__results-table-wrapper">
|
||||
<table class="sw-picker__results-table">
|
||||
<thead>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue