Styling fixes and Olden Era mmodule

This commit is contained in:
Jakub Zych
2026-05-21 15:00:12 +02:00
parent 4ceff4015a
commit dd3493921b
136 changed files with 1028 additions and 111 deletions

View File

@@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Services\TwitchHelix;
use App\Support\OldenEra;
use App\Support\RigData;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -18,13 +19,19 @@ use Throwable;
*/
class OnboardController extends Controller
{
public function show(RigData $rig, TwitchHelix $twitch)
public function show(RigData $rig, TwitchHelix $twitch, OldenEra $olden)
{
return view('onboard', [
'manifest' => $rig->loading(),
'telemetry' => $rig->telemetry(),
'trackCount' => $rig->playlistCount(),
'twitchEnabled' => $twitch->enabled(),
'oldenEra' => [
'gameId' => OldenEra::GAME_ID,
'assetBase' => OldenEra::URL,
'catalog' => $olden->catalog(),
'manifest' => $olden->manifest(),
],
]);
}
@@ -47,7 +54,7 @@ class OnboardController extends Controller
return response()->json(['matches' => $hits]);
}
public function submit(Request $request, RigData $rig, TwitchHelix $twitch): JsonResponse
public function submit(Request $request, RigData $rig, TwitchHelix $twitch, OldenEra $olden): JsonResponse
{
$data = $request->validate([
'game' => 'required|string|max:200',
@@ -76,6 +83,28 @@ class OnboardController extends Controller
json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n"
);
$oldenPath = null;
if (($manifest['gameId'] ?? null) === OldenEra::GAME_ID) {
$factions = array_keys($olden->catalog());
$extra = $request->validate([
'olden.color' => 'required|in:red,blue',
'olden.my.faction' => 'required|string|in:' . implode(',', $factions),
'olden.my.hero' => 'required|string|max:120',
'olden.enemy.faction' => 'required|string|in:' . implode(',', $factions),
'olden.enemy.hero' => 'required|string|max:120',
'olden.map' => 'nullable|string|max:200',
])['olden'];
$oldenPath = $olden->writeManifest([
'compiledAt' => gmdate('Y-m-d\TH:i:s\Z'),
'gameId' => OldenEra::GAME_ID,
'color' => $extra['color'],
'my' => $extra['my'],
'enemy' => $extra['enemy'],
'map' => isset($extra['map']) && $extra['map'] !== '' ? $extra['map'] : null,
]);
}
$twitchStatus = null;
if (!empty($data['pushTwitch']) && $twitch->enabled() && !empty($manifest['gameId'])) {
try {
@@ -88,10 +117,11 @@ class OnboardController extends Controller
}
return response()->json([
'ok' => true,
'manifest' => $manifest,
'twitch' => $twitchStatus,
'path' => str_replace(base_path() . '/', '', $path),
'ok' => true,
'manifest' => $manifest,
'twitch' => $twitchStatus,
'path' => str_replace(base_path() . '/', '', $path),
'oldenPath' => $oldenPath ? str_replace(base_path() . '/', '', $oldenPath) : null,
]);
}

View File

@@ -3,14 +3,22 @@
namespace App\Http\Controllers\Overlays;
use App\Http\Controllers\Controller;
use App\Support\OldenEra;
use App\Support\RigData;
class GameController extends Controller
{
public function show(RigData $rig)
public function show(RigData $rig, OldenEra $olden)
{
$manifest = $rig->loading();
$isOlden = ($manifest['gameId'] ?? null) === OldenEra::GAME_ID;
$oldenManifest = $isOlden ? $olden->manifest() : [];
return view('overlays.game', [
'manifest' => $rig->loading(),
'manifest' => $manifest,
'olden' => !empty($oldenManifest) ? $oldenManifest : null,
'oldenAssetBase' => OldenEra::URL,
'rigName' => (string) (config('rig.streamer_nick') ?: 'OPHI118'),
]);
}
}

View File

@@ -10,8 +10,9 @@ use RuntimeException;
* Thin Twitch Helix client. Replaces twitch-bot/search-game.py + set-channel.py
* for use from `php artisan rig:loading`.
*
* Requires TWITCH_CLIENT_ID + TWITCH_TOKEN (user OAuth token with the
* Requires TWITCH_CLIENT_ID + TWITCH_ACCESS_TOKEN (user OAuth token with the
* channel:manage:broadcast scope) and TWITCH_BROADCASTER_ID in .env.
* Legacy TWITCH_TOKEN is still accepted as a fallback (see config/rig.php).
*/
class TwitchHelix
{

View File

@@ -0,0 +1,78 @@
<?php
namespace App\Support;
/**
* Per-game extra manifest for Heroes of Might & Magic: Olden Era.
*
* `loading.json` keeps the generic stream metadata (game, subtitle, mic…).
* Olden Era specifics matchup factions, heroes, the player's team color,
* map name live in their own `olden-era-manifest.json` so the loading
* schema doesn't grow a new key every time a game gets bespoke chrome.
*/
class OldenEra
{
/** Twitch Helix category id for "Heroes of Might & Magic: Olden Era". */
public const GAME_ID = '1426873999';
/** Public URL prefix; resolved via the webapp/public/games symlink. */
public const URL = '/games/olden-era';
private ?array $catalogCache = null;
/**
* Faction {logo URL, heroes list}. Built by globbing
* resources/games/olden-era/{factions,heroes/<Faction>}/*.png.
*/
public function catalog(): array
{
if ($this->catalogCache !== null) return $this->catalogCache;
$root = resource_path('games/olden-era');
$out = [];
foreach (glob($root . '/factions/*.png') ?: [] as $factionPng) {
$faction = pathinfo($factionPng, PATHINFO_FILENAME);
$heroes = [];
foreach (glob($root . '/heroes/' . $faction . '/*.png') ?: [] as $heroPng) {
$name = pathinfo($heroPng, PATHINFO_FILENAME);
$heroes[] = [
'name' => $name,
'portrait' => self::URL . '/heroes/' . rawurlencode($faction) . '/' . rawurlencode($name) . '.png',
];
}
usort($heroes, fn($a, $b) => strcasecmp($a['name'], $b['name']));
$out[$faction] = [
'logo' => self::URL . '/factions/' . rawurlencode($faction) . '.png',
'heroes' => $heroes,
];
}
ksort($out);
return $this->catalogCache = $out;
}
public function manifest(): array
{
$path = $this->manifestPath();
if (!is_file($path)) return [];
$parsed = json_decode((string) @file_get_contents($path), true);
return is_array($parsed) ? $parsed : [];
}
public function writeManifest(array $data): string
{
$path = $this->manifestPath();
@mkdir(dirname($path), 0775, true);
file_put_contents(
$path,
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n"
);
return $path;
}
public function manifestPath(): string
{
return rtrim(config('rig.storage_data'), '/') . '/olden-era-manifest.json';
}
}